dazscript-framework 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/babel/babel.config.js +33 -0
  2. package/babel/trace-babel-plugin.js +243 -0
  3. package/babel/trace-log-babel-plugin.js +164 -0
  4. package/common/core.ts +2 -0
  5. package/common/log.ts +57 -0
  6. package/common/trace.ts +26 -0
  7. package/core/action-decorator.ts +16 -0
  8. package/dialog/basic-dialog.ts +32 -0
  9. package/dialog/builders/button-builder.ts +53 -0
  10. package/dialog/builders/checkbox-builder.ts +30 -0
  11. package/dialog/builders/combo-box-builder.ts +36 -0
  12. package/dialog/builders/combo-edit-builder.ts +36 -0
  13. package/dialog/builders/dialog-builder.ts +49 -0
  14. package/dialog/builders/groupbox-builder.ts +73 -0
  15. package/dialog/builders/label-builder.ts +18 -0
  16. package/dialog/builders/layout-builder.ts +46 -0
  17. package/dialog/builders/line-edit-builder.ts +118 -0
  18. package/dialog/builders/list-view-builder.ts +327 -0
  19. package/dialog/builders/node-selection-builder.ts +44 -0
  20. package/dialog/builders/popup-menu-builder.ts +47 -0
  21. package/dialog/builders/radio-builder.ts +43 -0
  22. package/dialog/builders/splitter-builder.ts +90 -0
  23. package/dialog/builders/tab-builder.ts +106 -0
  24. package/dialog/builders/widget-builder.ts +109 -0
  25. package/dialog/builders/widgets-builder.ts +119 -0
  26. package/dialog/input-dialog.ts +28 -0
  27. package/dialog/input-validator.ts +9 -0
  28. package/dialog/shared.ts +8 -0
  29. package/helpers/action-helper.ts +81 -0
  30. package/helpers/array-helper.ts +145 -0
  31. package/helpers/camera-helper.ts +14 -0
  32. package/helpers/custom-action-helper.ts +176 -0
  33. package/helpers/file-helper.ts +90 -0
  34. package/helpers/input-helper.ts +19 -0
  35. package/helpers/list-view-helper.ts +89 -0
  36. package/helpers/menu-helper.ts +29 -0
  37. package/helpers/message-box-helper.ts +16 -0
  38. package/helpers/node-helper.ts +216 -0
  39. package/helpers/number-helper.ts +11 -0
  40. package/helpers/numeric-property-helper.ts +92 -0
  41. package/helpers/object-helper.ts +3 -0
  42. package/helpers/pane-helper.ts +21 -0
  43. package/helpers/progress-helper.ts +34 -0
  44. package/helpers/property-helper.ts +53 -0
  45. package/helpers/record-helper.ts +16 -0
  46. package/helpers/scene-helper.ts +96 -0
  47. package/helpers/script-helper.ts +16 -0
  48. package/helpers/skeleton-helper.ts +27 -0
  49. package/helpers/splitter-helper.ts +9 -0
  50. package/helpers/string-helper.ts +28 -0
  51. package/helpers/surface-helper.ts +6 -0
  52. package/helpers/undo-helper.ts +7 -0
  53. package/helpers/viewport-helper.ts +9 -0
  54. package/lib/delayed.ts +39 -0
  55. package/lib/dz-dump.ts +121 -0
  56. package/lib/global.ts +5 -0
  57. package/lib/guid.ts +3 -0
  58. package/lib/observable.ts +94 -0
  59. package/lib/set.ts +25 -0
  60. package/lib/settings.ts +104 -0
  61. package/models/custom-action.ts +12 -0
  62. package/models/frame-keys.ts +68 -0
  63. package/package.json +48 -0
  64. package/shared/base-script.ts +30 -0
  65. package/shared/install-generator.js +185 -0
  66. package/shared/set-keyboard-shortcut.ts +103 -0
  67. package/webpack.config.js +48 -0
@@ -0,0 +1,36 @@
1
+ import { Observable } from '@dsf/lib/observable';
2
+ import { WidgetBuilderBase, createWidget } from './widget-builder';
3
+ import { WidgetBuilderContext } from './widgets-builder';
4
+
5
+ export class ComboEditBuilder extends WidgetBuilderBase<DzComboEdit> {
6
+ constructor(context: WidgetBuilderContext) {
7
+ super(createWidget(context).build(DzComboEdit));
8
+ }
9
+
10
+ items(items: string[]): this {
11
+ this.widget.insertItems(0, items)
12
+ return this
13
+ }
14
+
15
+ changed(bind: Observable<string>): this {
16
+ this.widget.text = bind.value
17
+ this.widget.itemChanged.scriptConnect((text) => {
18
+ bind.value = text
19
+ })
20
+ bind.connect((text) => {
21
+ this.widget.text = text
22
+ })
23
+ return this
24
+ }
25
+
26
+ edited(bind: Observable<string>): this {
27
+ this.widget.text = bind.value
28
+ this.widget.textEdited.scriptConnect((text) => {
29
+ bind.value = text
30
+ })
31
+ bind.connect((text) => {
32
+ this.widget.text = text
33
+ })
34
+ return this
35
+ }
36
+ }
@@ -0,0 +1,49 @@
1
+ import { DialogProperties } from '../shared';
2
+ import { WidgetBuilderContext, WidgetsBuilder } from './widgets-builder';
3
+
4
+ export class DialogBuilder extends WidgetsBuilder {
5
+ public properties: DialogProperties
6
+
7
+ constructor(private title: string, private id?: string) {
8
+ super(new WidgetBuilderContext(new DzBasicDialog()))
9
+ this.properties = new DialogProperties()
10
+ }
11
+
12
+ options(properties: DialogProperties) {
13
+ this.properties = properties
14
+ }
15
+
16
+ build(setup: () => void): DzBasicDialog {
17
+ this.init()
18
+ setup()
19
+ this.resize()
20
+ return this.context.dialog
21
+ }
22
+
23
+ private init() {
24
+ this.context.dialog.caption = this.title
25
+ let dialogWidget = this.context.dialog.getWidget()
26
+ var key = (this.id ?? this.context.dialog.caption).replace(/ /g, "") + "Dlg"
27
+ dialogWidget.objectName = key
28
+ }
29
+
30
+ private resize() {
31
+ let dialogWidget = this.context.dialog.getWidget();
32
+ let sizeHint = dialogWidget.minimumSizeHint;
33
+ let height = this.properties.height ?? sizeHint.height;
34
+ this.context.dialog.minHeight = sizeHint.height;
35
+ this.context.dialog.height = height;
36
+
37
+ if (this.properties.width) this.context.dialog.width = this.properties.width;
38
+
39
+ if (this.properties.resizable === true) {
40
+ this.context.dialog.sizeGripEnabled = true;
41
+ }
42
+ else {
43
+ if (this.properties.width)
44
+ this.context.dialog.setFixedWidth(this.properties.width);
45
+ this.context.dialog.setFixedHeight(height);
46
+ }
47
+ }
48
+ }
49
+
@@ -0,0 +1,73 @@
1
+ import { Observable } from '@dsf/lib/observable';
2
+ import { Direction } from '../shared';
3
+ import LayoutBuilder from './layout-builder';
4
+ import { IWidgetBuilder, createWidget } from './widget-builder';
5
+ import { WidgetBuilderContext } from './widgets-builder';
6
+
7
+ export default class GroupBoxBuilder implements IWidgetBuilder<DzGroupBox> {
8
+ private _title: Observable<string>
9
+ private _direction: Direction = 'vertical'
10
+ private _style: { flat?: boolean }
11
+ private _visible: Observable<boolean>
12
+
13
+ constructor(private context: WidgetBuilderContext) {
14
+ }
15
+
16
+ title(text: string | Observable<string>): this {
17
+ this._title = typeof text === 'string'
18
+ ? new Observable(text)
19
+ : text
20
+ return this
21
+ }
22
+
23
+ vertical(): this {
24
+ this._direction = 'vertical'
25
+ return this
26
+ }
27
+
28
+ horizontal(): this {
29
+ this._direction = 'horizontal'
30
+ return this
31
+ }
32
+
33
+ style(options: { flat?: boolean }): this {
34
+ this._style = options
35
+ return this
36
+ }
37
+
38
+ visible(value: boolean | Observable<boolean>): this {
39
+ this._visible = typeof value === 'boolean'
40
+ ? new Observable(value)
41
+ : value
42
+ return this
43
+ }
44
+
45
+ build(then?: (layout: DzVBoxLayout | DzHBoxLayout) => void): DzGroupBox {
46
+ let groupBox = createWidget(this.context).build(DzGroupBox)
47
+
48
+ groupBox.title = this._title?.value
49
+ groupBox.flat = this._style?.flat
50
+
51
+ this._title?.connect((text) => {
52
+ groupBox.title = text
53
+ })
54
+
55
+ new LayoutBuilder(this.context)
56
+ .parent(groupBox)
57
+ .direction(this._direction)
58
+ .build(then)
59
+
60
+ if (this._visible) {
61
+ if (this._visible.value === false)
62
+ groupBox.hide()
63
+ this._visible.connect((visible) => {
64
+ if (visible)
65
+ groupBox.show()
66
+ else
67
+ groupBox.hide()
68
+ })
69
+ }
70
+
71
+ return groupBox
72
+ }
73
+ }
@@ -0,0 +1,18 @@
1
+ import { WidgetBuilderBase, createWidget } from './widget-builder';
2
+ import { WidgetBuilderContext } from './widgets-builder';
3
+
4
+ export default class LabelBuilder extends WidgetBuilderBase<DzLabel> {
5
+ constructor(context: WidgetBuilderContext) {
6
+ super(createWidget(context).build(DzLabel));
7
+
8
+ }
9
+
10
+ text(text: string): this {
11
+ this.widget.text = text;
12
+ return this;
13
+ }
14
+
15
+ build(): DzLabel {
16
+ return this.widget;
17
+ }
18
+ }
@@ -0,0 +1,46 @@
1
+ import { Direction } from '../shared';
2
+ import { WidgetBuilderContext } from './widgets-builder';
3
+
4
+ export default class LayoutBuilder {
5
+ private widgetParent: DzWidget | DzLayout | null;
6
+ private _direction: Direction = 'vertical';
7
+
8
+ constructor(protected context: WidgetBuilderContext) { }
9
+
10
+ static create(context: WidgetBuilderContext): LayoutBuilder {
11
+ return new LayoutBuilder(context)
12
+ }
13
+
14
+ direction(direction: Direction): this {
15
+ this._direction = direction;
16
+ return this;
17
+ }
18
+
19
+ parent(parent: DzWidget | DzLayout): this {
20
+ this.widgetParent = parent;
21
+ return this;
22
+ }
23
+
24
+ build(then?: (layout: DzVBoxLayout | DzHBoxLayout) => void): DzVBoxLayout | DzHBoxLayout {
25
+ let parent = this.widgetParent ?? this.context.parent;
26
+ let type = this.getLayoutFor(this._direction);
27
+ let layout = new type(parent);
28
+
29
+ let currentLayout = this.context.layout;
30
+ this.context.layout = layout;
31
+
32
+ if (parent.inherits("DzWidget") && (parent as any).addLayout)
33
+ (parent as DzBasicDialog).addLayout(layout);
34
+
35
+ then?.(layout);
36
+
37
+ this.context.layout = currentLayout;
38
+ return layout;
39
+ }
40
+
41
+
42
+ private getLayoutFor(direction: string): new (arg: any) => DzVBoxLayout | DzHBoxLayout {
43
+ return direction === 'horizontal' ? DzHBoxLayout : DzVBoxLayout;
44
+ }
45
+ }
46
+
@@ -0,0 +1,118 @@
1
+ import { Observable } from '@dsf/lib/observable';
2
+ import { InputValidator } from '../input-validator';
3
+ import { WidgetBuilderBase, createWidget } from './widget-builder';
4
+ import { WidgetBuilderContext } from './widgets-builder';
5
+
6
+ export default class LineEditBuilder extends WidgetBuilderBase<DzLineEdit> {
7
+ private inputValidator: InputValidator
8
+ private minValue?: number
9
+ private maxValue?: number
10
+
11
+ constructor(context: WidgetBuilderContext) {
12
+ super(createWidget(context).build(DzLineEdit))
13
+ }
14
+
15
+ placeholder(text: string): this {
16
+ this.widget.placeholderText = text
17
+ return this
18
+ }
19
+
20
+ focus(): this {
21
+ this.widget.getWidget().setFocus()
22
+ return this
23
+ }
24
+
25
+ validator(validator: InputValidator | null): this {
26
+ if (validator === null) return this
27
+ this.inputValidator = validator
28
+ let settings = new DzSettings()
29
+ settings.setStringValue("validator", InputValidator[validator]);
30
+ this.widget.setValidator(settings)
31
+ return this
32
+ }
33
+
34
+ /**
35
+ * Sets the min value of the input when validator is set to in or float
36
+ * @param minValue
37
+ * @returns
38
+ */
39
+ min(minValue: number): this {
40
+ this.minValue = minValue
41
+ return this
42
+ }
43
+
44
+ /**
45
+ * Sets the max value of the input when validator is set to in or float
46
+ * @param maxValue
47
+ * @returns
48
+ */
49
+ max(maxValue: number): this {
50
+ this.maxValue = maxValue
51
+ return this
52
+ }
53
+
54
+ /**
55
+ * Clamps the input to min and max values. Only works when validator is set to int or float
56
+ * @param min
57
+ * @param max
58
+ * @returns
59
+ */
60
+ clamp(min: number, max: number): this {
61
+ return this.min(min).max(max)
62
+ }
63
+
64
+ readOnly(onOff: boolean | Observable<boolean>): this {
65
+ if (typeof onOff === 'boolean') {
66
+ this.widget.readOnly = onOff
67
+ }
68
+ else {
69
+ this.widget.readOnly = onOff.value
70
+ onOff.connect((value) => {
71
+ this.widget.readOnly = value
72
+ })
73
+ }
74
+ return this
75
+ }
76
+
77
+ text(text_: string | Observable<string>): this {
78
+ if (typeof text_ === 'string') {
79
+ this.widget.text = text_
80
+ }
81
+ else if (text_ instanceof Observable) {
82
+ this.widget.text = text_.value;
83
+ this.widget.textChanged.scriptConnect((text: string) => {
84
+ text_.value = this.clampValue(text);
85
+ });
86
+ text_.connect((text: string) => {
87
+ this.widget.text = text;
88
+ });
89
+ }
90
+
91
+ return this;
92
+ }
93
+
94
+ private clampValue(text: string): string {
95
+ if (this.inputValidator === InputValidator.int || this.inputValidator === InputValidator.float) {
96
+ let value = Number(text);
97
+ if (!isNaN(value)) {
98
+ if (this.minValue !== undefined) {
99
+ value = Math.max(value, this.minValue);
100
+ }
101
+ if (this.maxValue !== undefined) {
102
+ value = Math.min(value, this.maxValue);
103
+ }
104
+ return String(value);
105
+ }
106
+ }
107
+ else if (this.maxValue && text.length > this.maxValue)
108
+ text = text[this.maxValue]
109
+
110
+ return text;
111
+ }
112
+
113
+
114
+ build(then?: (lineEdit: DzLineEdit) => void): DzLineEdit {
115
+ then?.(this.widget);
116
+ return this.widget;
117
+ }
118
+ }
@@ -0,0 +1,327 @@
1
+ import { warn } from '@dsf/common/log';
2
+ import { clearColumns, filter, getDataItem, setDataItem } from '@dsf/helpers/list-view-helper';
3
+ import { Delayed } from '@dsf/lib/delayed';
4
+ import { Observable } from '@dsf/lib/observable';
5
+ import { TreeNode } from 'shared/models/tree-node';
6
+ import { IWidgetBuilder, createWidget } from './widget-builder';
7
+ import { WidgetBuilderContext } from './widgets-builder';
8
+
9
+ type ListViewFilterOptions = {
10
+ keywords: Observable<string>,
11
+ field: (listItem: DzListViewItem) => string,
12
+ selectOnFilter?: boolean,
13
+ filters?: (viewItem: DzListViewItem) => boolean
14
+ }
15
+
16
+ export enum ListViewRefreshOptions {
17
+ All,
18
+ Filters
19
+ }
20
+
21
+ export class ListViewBuilder<TItem, TData> implements IWidgetBuilder<DzListView> {
22
+
23
+ private readonly context: ListViewBuilderContext<TItem, TData>
24
+
25
+ constructor(dialogContext: WidgetBuilderContext) {
26
+ this.context = new ListViewBuilderContext(dialogContext)
27
+ }
28
+
29
+ visible(value: boolean | Observable<boolean>): this {
30
+ this.context.visible = typeof value === 'boolean'
31
+ ? new Observable(value)
32
+ : value
33
+ return this
34
+ }
35
+
36
+ /**
37
+ * Binds the list rows to a collection of items
38
+ * @param items
39
+ * @returns
40
+ */
41
+ items(items: Observable<TreeNode<TItem>[]>): ListViewBindBuilder<TItem, TData> {
42
+ this.context.items = items
43
+ return new ListViewBindBuilder(this.context)
44
+ }
45
+
46
+ /**
47
+ * Build an item row with the provided function
48
+ * @param build the function to use for building a row
49
+ * @returns returns a ListViewItem or DzCheckListItem
50
+ */
51
+ row(build: (item: TreeNode<TItem>, parent: DzListView | DzListViewItem, id?: number) => DzListViewItem): this {
52
+ this.context.rowBuilder = build
53
+ return this
54
+ }
55
+
56
+ sorting(sort: boolean | number): this {
57
+ if (typeof sort === 'boolean')
58
+ this.context.sorting = sort ? 0 : -1
59
+ else
60
+ this.context.sorting = sort
61
+ return this
62
+ }
63
+
64
+ expanded(onOff: boolean | Observable<boolean>): this {
65
+ if (typeof onOff === 'boolean')
66
+ this.context.expanded = new Observable(onOff)
67
+ else
68
+ this.context.expanded = onOff
69
+ return this
70
+ }
71
+
72
+ flat(onOff: boolean | Observable<boolean>): this {
73
+ this.context.flat = typeof onOff === 'boolean'
74
+ ? new Observable(onOff)
75
+ : onOff
76
+ return this
77
+ }
78
+
79
+ doubleClicked(bind: Observable<TData>): this {
80
+ this.context.doubleClicked = bind
81
+ return this
82
+ }
83
+
84
+ refresh(when: Observable<any>, what: ListViewRefreshOptions = ListViewRefreshOptions.All): this {
85
+ this.context.refreshWhen = when
86
+ this.context.refreshWhat = what
87
+ return this
88
+ }
89
+
90
+ contextMenu(fn: (listView: DzListView, item: DzListViewItem, pos: Point) => DzPopupMenu): this {
91
+ this.context.contextMenu = fn
92
+ return this
93
+ }
94
+
95
+ build(then?: (listView: DzListView) => void): DzListView {
96
+ let listView = build(this.context)
97
+ then?.(listView)
98
+ return listView
99
+ }
100
+ }
101
+
102
+ class ListViewBuilderContext<TItem, TData> {
103
+ columns: Observable<string[]> = new Observable([])
104
+ columnsWidth: (index: number, width: number) => number
105
+ items: Observable<TreeNode<TItem>[]> = new Observable([])
106
+ text: (item: TreeNode<TItem>) => string[]
107
+ data: (item: TreeNode<TItem>) => TData
108
+ sorting: number = 0
109
+ selected: Observable<TData>
110
+ filter: ListViewFilterOptions
111
+ doubleClicked: Observable<TData>
112
+ rowBuilder: (item: TreeNode<TItem>, parent: DzListView | DzListViewItem, id?: number) => DzListViewItem
113
+ contextMenu: (listView: DzListView, item: DzListViewItem, pos: Point) => DzPopupMenu
114
+ refreshWhen: Observable<void>
115
+ expanded: Observable<boolean>
116
+ visible: Observable<boolean> = new Observable(true)
117
+ decorated: boolean
118
+ flat: Observable<boolean>
119
+ refreshWhat: ListViewRefreshOptions = ListViewRefreshOptions.All
120
+ constructor(public readonly dialogContext: WidgetBuilderContext) { }
121
+ }
122
+
123
+ class ListViewBindBuilder<TItem, TData> {
124
+ constructor(private context: ListViewBuilderContext<TItem, TData>) { }
125
+
126
+ /**
127
+ * Sepecify the text to display on each column
128
+ * @param columns
129
+ * @returns
130
+ */
131
+ columns(columns: string[] | Observable<string[]>, width?: (index: number, width: number) => number): this {
132
+ this.context.columns = columns instanceof Observable
133
+ ? columns
134
+ : new Observable(columns)
135
+ this.context.columnsWidth = width
136
+ return this
137
+ }
138
+
139
+ /**
140
+ * Callback for extracting the text to display on each column from every item in the list
141
+ * @param from the callback function to extract the text for each item
142
+ * @returns a string array for every column text to be display for a given item
143
+ */
144
+ text(from: (item: TreeNode<TItem>) => string[]): this {
145
+ this.context.text = from
146
+ return this
147
+ }
148
+
149
+ /**
150
+ * Callback to specify which part of the item will be stored in each row as data. It can be
151
+ * the whole item or a property of the item
152
+ * @param from the callback function to specify what to store as data
153
+ * @returns the data to be stored in each row for a given item
154
+ */
155
+ data(from: (item: TreeNode<TItem>) => TData): this {
156
+ this.context.data = from
157
+ return this
158
+ }
159
+
160
+ /**
161
+ * Bind the data of a selected row in the list to an object
162
+ * @param bind
163
+ * @returns
164
+ */
165
+ selected(bind: Observable<TData>): this {
166
+ this.context.selected = bind
167
+ return this
168
+ }
169
+
170
+ filter(options: ListViewFilterOptions): this {
171
+ this.context.filter = options
172
+ return this
173
+ }
174
+
175
+ build(then?: (listView: DzListView) => void): DzListView {
176
+ let listView = build(this.context)
177
+ then?.(listView)
178
+ return listView
179
+ }
180
+ }
181
+
182
+ const build = <TItem, TData>(context: ListViewBuilderContext<TItem, TData>): DzListView => {
183
+ const listView = createWidget(context.dialogContext).build(DzListView)
184
+ let rowId = -1
185
+
186
+ if (context.visible) {
187
+ if (context.visible.value === false)
188
+ listView.hide()
189
+ context.visible.connect((visible) => {
190
+ if (visible)
191
+ listView.show()
192
+ else
193
+ listView.hide()
194
+ })
195
+ }
196
+
197
+ const buildColumns = (columns: string[]) => {
198
+ clearColumns(listView)
199
+ columns.forEach(column => {
200
+ listView.addColumn(column)
201
+ })
202
+ }
203
+
204
+ const buildItem = (item: TreeNode<TItem>, parent: DzListView | DzListViewItem) => {
205
+ rowId++
206
+ parent = context.flat?.value === true ? listView : parent
207
+
208
+ let listItem = context.rowBuilder ? context.rowBuilder(item, parent, rowId) : new DzListViewItem(parent, rowId)
209
+ if (!listItem) return
210
+
211
+ listItem.open = context.expanded?.value === true
212
+
213
+ context.text(item).forEach((text, idx) => {
214
+ let data = context.data?.(item)
215
+ if (data) {
216
+ setDataItem(listItem, data)
217
+ }
218
+ if (!text || idx >= context.columns.value.length)
219
+ return;
220
+ listItem.setText(idx, text)
221
+ })
222
+
223
+ item.children.forEach((child) => {
224
+ context.decorated = true
225
+ buildItem(child, listItem)
226
+ })
227
+ }
228
+
229
+ const filterList = (keywords?: string) => {
230
+ filter(listView, context.filter.field, keywords ?? context.filter?.keywords?.value, { selectOnFilter: context.filter.selectOnFilter ?? true, filters: context.filter.filters })
231
+ }
232
+
233
+ const buildList = (items: TreeNode<TItem>[], selectedId?: number) => {
234
+ rowId = -1
235
+ if (!context.text)
236
+ return warn('No text function provided for list builder')
237
+
238
+ listView.clear()
239
+ context.columns?.value.forEach((_, index) => {
240
+ listView.setColumnWidth(index, 0)
241
+ })
242
+ listView.allColumnsShowFocus = true
243
+
244
+ if (context.visible.value === false)
245
+ return
246
+
247
+ if (!items) return
248
+
249
+ items.forEach((item) => {
250
+ buildItem(item, listView)
251
+ })
252
+
253
+ if (context.columnsWidth) {
254
+ context.columns?.value.forEach((_, index) => {
255
+ listView.setColumnWidth(index, context.columnsWidth(index, listView.columnWidth(index)))
256
+ })
257
+ }
258
+
259
+ listView.rootIsDecorated = context.decorated
260
+ listView.setSorting(context.sorting)
261
+ if (context.sorting >= 0) listView.sort()
262
+
263
+ if (context.filter?.keywords.value || context.filter?.filters)
264
+ filterList()
265
+
266
+ if (selectedId) {
267
+ listView.getItems(DzListView.All).forEach(item => {
268
+ if (item.id === selectedId) {
269
+ listView.setSelected(item, true)
270
+ listView.setCurrentItem(item)
271
+ listView.ensureItemVisible(item)
272
+ }
273
+ })
274
+ }
275
+ }
276
+
277
+ const onSelectionChanged = (callback: (item: DzListViewItem, data: TData) => void) => {
278
+ (listView as any)["selectionChanged()"].scriptConnect(() => {
279
+ callback(listView.selectedItem(), getDataItem(listView.selectedItem()))
280
+ })
281
+ }
282
+
283
+ buildColumns(context.columns.value)
284
+ context.columns.connect((columns) => {
285
+ buildColumns(columns)
286
+ })
287
+
288
+ buildList(context.items?.value)
289
+ context.items.connect((items) => {
290
+ buildList(items, listView.selectedItem()?.id)
291
+ })
292
+
293
+ context.refreshWhen?.connect(() => {
294
+ if (context.refreshWhat === ListViewRefreshOptions.All)
295
+ buildList(context.items?.value, listView.selectedItem()?.id)
296
+ else {
297
+ filterList()
298
+ }
299
+
300
+ })
301
+
302
+ if (context.selected) {
303
+ onSelectionChanged((_, data) => {
304
+ context.selected.value = data
305
+ })
306
+ }
307
+
308
+ if (context.filter) {
309
+ context.filter.keywords.connect((keywords) => {
310
+ new Delayed(() => {
311
+ filterList(keywords)
312
+ }, 100, 400).trigger()
313
+ })
314
+ }
315
+
316
+ if (context.doubleClicked) {
317
+ listView.doubleClicked.scriptConnect((listItem) => {
318
+ context.doubleClicked.value = listItem?.getDataItem('data')
319
+ })
320
+ }
321
+
322
+ listView.contextMenuRequested.scriptConnect((item: DzListViewItem, pos: Point) => {
323
+ context.contextMenu?.(listView, item, pos).exec(pos.cursorPos(), 0)
324
+ })
325
+
326
+ return listView
327
+ }