dazscript-framework 1.0.9 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/scripts/install-generator.js +1 -0
- package/package.json +1 -1
- package/src/dialog/builders/dialog-builder.ts +7 -1
- package/src/dialog/builders/dialog-header-builder.ts +23 -9
- package/src/dialog/builders/groupbox-builder.ts +7 -0
- package/src/dialog/builders/list-view-builder.ts +39 -7
- package/src/dialog/builders/tab-builder.ts +7 -0
- package/src/dialog/builders/widget-builder.ts +6 -0
- package/src/dialog/shared.ts +2 -0
- package/src/scripts/install-generator.test.ts +14 -0
package/README.md
CHANGED
|
@@ -302,8 +302,9 @@ Custom action icons are selected from `action(...)` metadata and sibling image f
|
|
|
302
302
|
Setup dialog header assets are optional and are discovered beside `src/Setup.dsa.ts`:
|
|
303
303
|
|
|
304
304
|
1. `src/Setup.header.png`
|
|
305
|
-
2. `src/Setup.png`
|
|
306
|
-
3. `src/Setup.
|
|
305
|
+
2. `src/Setup.tip.png`
|
|
306
|
+
3. `src/Setup.png`
|
|
307
|
+
4. `src/Setup.dsa.png` legacy fallback
|
|
307
308
|
|
|
308
309
|
Header text can be placed in `src/Setup.header.html`, `src/Setup.header.md`, or `src/Setup.header.txt`, with `src/Setup.html`, `src/Setup.md`, and `src/Setup.txt` as script-named fallbacks. The installer generator embeds that text into `Setup.dsa.ts`, so Daz Studio does not need to read the text file at setup time. The setup dialog renders the header body with `DzTextBrowser` rich text support; no Markdown conversion is performed. The image remains a deployed PNG asset and is resolved relative to the generated setup script at runtime.
|
|
309
310
|
|
|
@@ -279,6 +279,7 @@ function toPosix(filePath) {
|
|
|
279
279
|
function resolveSetupHeaderImage(workdir) {
|
|
280
280
|
const candidates = [
|
|
281
281
|
path.join(workdir, 'src', 'Setup.header.png'),
|
|
282
|
+
path.join(workdir, 'src', 'Setup.tip.png'),
|
|
282
283
|
path.join(workdir, 'src', 'Setup.png'),
|
|
283
284
|
path.join(workdir, 'src', 'Setup.dsa.png'),
|
|
284
285
|
];
|
package/package.json
CHANGED
|
@@ -30,11 +30,17 @@ export class DialogBuilder extends WidgetsBuilder {
|
|
|
30
30
|
private resize() {
|
|
31
31
|
let dialogWidget = this.context.dialog.getWidget();
|
|
32
32
|
let sizeHint = dialogWidget.minimumSizeHint;
|
|
33
|
+
let maxHeight = this.properties.maxHeight;
|
|
33
34
|
let height = this.properties.height ?? sizeHint.height;
|
|
34
|
-
|
|
35
|
+
if (maxHeight !== undefined && height > maxHeight) height = maxHeight;
|
|
36
|
+
this.context.dialog.minHeight = maxHeight !== undefined
|
|
37
|
+
? Math.min(sizeHint.height, maxHeight)
|
|
38
|
+
: sizeHint.height;
|
|
35
39
|
this.context.dialog.height = height;
|
|
36
40
|
|
|
37
41
|
if (this.properties.width) this.context.dialog.width = this.properties.width;
|
|
42
|
+
if (this.properties.maxWidth !== undefined) this.context.dialog.maxWidth = this.properties.maxWidth;
|
|
43
|
+
if (maxHeight !== undefined) this.context.dialog.maxHeight = maxHeight;
|
|
38
44
|
|
|
39
45
|
if (this.properties.resizable === true) {
|
|
40
46
|
this.context.dialog.sizeGripEnabled = true;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import LayoutBuilder from './layout-builder'
|
|
2
2
|
import { createWidget } from './widget-builder'
|
|
3
3
|
import { WidgetBuilderContext } from './widgets-builder'
|
|
4
|
+
import { getScriptPath } from '@dsf/helpers/script-helper'
|
|
4
5
|
|
|
5
6
|
export type DialogHeaderOptions = {
|
|
6
7
|
image?: Pixmap
|
|
@@ -20,13 +21,21 @@ const escapeHtml = (value: string): string =>
|
|
|
20
21
|
.replace(/'/g, ''')
|
|
21
22
|
.replace(/\r?\n/g, '<br>')
|
|
22
23
|
|
|
24
|
+
const resolveImagePath = (filePath: string): string => {
|
|
25
|
+
const value = String(filePath ?? '').replace(/\\/g, '/')
|
|
26
|
+
if (!value) return ''
|
|
27
|
+
if (value.indexOf(':') >= 0 || value.charAt(0) === '/') return value
|
|
28
|
+
|
|
29
|
+
return `${getScriptPath()}/${value.replace(/^\.\//, '')}`
|
|
30
|
+
}
|
|
31
|
+
|
|
23
32
|
export class DialogHeaderBuilder {
|
|
24
33
|
constructor(
|
|
25
34
|
private readonly context: WidgetBuilderContext,
|
|
26
35
|
private readonly options: DialogHeaderOptions
|
|
27
36
|
) { }
|
|
28
37
|
|
|
29
|
-
build():
|
|
38
|
+
build(): DzWidget {
|
|
30
39
|
const height = this.options.height ?? 96
|
|
31
40
|
const imageWidth = this.options.imageWidth ?? height
|
|
32
41
|
const html = this.options.html ?? (
|
|
@@ -35,18 +44,23 @@ export class DialogHeaderBuilder {
|
|
|
35
44
|
: ''
|
|
36
45
|
)
|
|
37
46
|
|
|
38
|
-
return
|
|
39
|
-
.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
this.
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
return createWidget(this.context).build(DzWidget, (container) => {
|
|
48
|
+
container.setFixedHeight(height)
|
|
49
|
+
|
|
50
|
+
LayoutBuilder
|
|
51
|
+
.create(this.context)
|
|
52
|
+
.parent(container)
|
|
53
|
+
.direction('horizontal')
|
|
54
|
+
.build(() => {
|
|
55
|
+
this.buildImage(height, imageWidth)
|
|
56
|
+
this.buildText(html, height)
|
|
57
|
+
})
|
|
58
|
+
})
|
|
45
59
|
}
|
|
46
60
|
|
|
47
61
|
private buildImage(height: number, imageWidth: number): void {
|
|
48
62
|
const pixmap = this.options.image ?? (
|
|
49
|
-
this.options.imagePath ? new Pixmap(this.options.imagePath) : null
|
|
63
|
+
this.options.imagePath ? new Pixmap(resolveImagePath(this.options.imagePath)) : null
|
|
50
64
|
)
|
|
51
65
|
if (!pixmap) return
|
|
52
66
|
|
|
@@ -13,6 +13,7 @@ export default class GroupBoxBuilder implements IWidgetBuilder<DzGroupBox> {
|
|
|
13
13
|
private _columns: number = 1
|
|
14
14
|
private _height: number | null = null
|
|
15
15
|
private _minHeight: number | null = null
|
|
16
|
+
private _maxHeight: number | null = null
|
|
16
17
|
|
|
17
18
|
constructor(private context: WidgetBuilderContext) {
|
|
18
19
|
}
|
|
@@ -73,6 +74,11 @@ export default class GroupBoxBuilder implements IWidgetBuilder<DzGroupBox> {
|
|
|
73
74
|
return this
|
|
74
75
|
}
|
|
75
76
|
|
|
77
|
+
maxHeight(value: number): this {
|
|
78
|
+
this._maxHeight = value
|
|
79
|
+
return this
|
|
80
|
+
}
|
|
81
|
+
|
|
76
82
|
columns(value: number): this {
|
|
77
83
|
this._columns = value
|
|
78
84
|
return this
|
|
@@ -86,6 +92,7 @@ export default class GroupBoxBuilder implements IWidgetBuilder<DzGroupBox> {
|
|
|
86
92
|
groupBox.columns = this._columns
|
|
87
93
|
if (this._height !== null) groupBox.height = this._height
|
|
88
94
|
if (this._minHeight !== null) groupBox.minHeight = this._minHeight
|
|
95
|
+
if (this._maxHeight !== null) groupBox.maxHeight = this._maxHeight
|
|
89
96
|
|
|
90
97
|
this._title?.connect((text) => {
|
|
91
98
|
groupBox.title = text
|
|
@@ -44,6 +44,11 @@ export class ListViewBuilder<TItem, TData> implements IWidgetBuilder<DzListView>
|
|
|
44
44
|
return this
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
maxHeight(value: number): this {
|
|
48
|
+
this.context.maxHeight = value
|
|
49
|
+
return this
|
|
50
|
+
}
|
|
51
|
+
|
|
47
52
|
/**
|
|
48
53
|
* Binds the list rows to a collection of items
|
|
49
54
|
* @param items
|
|
@@ -135,6 +140,7 @@ class ListViewBuilderContext<TItem, TData> {
|
|
|
135
140
|
visible: Observable<boolean> = new Observable(true)
|
|
136
141
|
height: number | null = null
|
|
137
142
|
minHeight: number | null = null
|
|
143
|
+
maxHeight: number | null = null
|
|
138
144
|
decorated: boolean
|
|
139
145
|
flat: Observable<boolean>
|
|
140
146
|
refreshWhat: ListViewRefreshOptions = ListViewRefreshOptions.All
|
|
@@ -228,6 +234,10 @@ const build = <TItem, TData>(context: ListViewBuilderContext<TItem, TData>): DzL
|
|
|
228
234
|
listView.minHeight = context.minHeight
|
|
229
235
|
}
|
|
230
236
|
|
|
237
|
+
if (context.maxHeight !== null) {
|
|
238
|
+
listView.maxHeight = context.maxHeight
|
|
239
|
+
}
|
|
240
|
+
|
|
231
241
|
const buildColumns = (columns: string[]) => {
|
|
232
242
|
clearColumns(listView)
|
|
233
243
|
columns.forEach(column => {
|
|
@@ -320,22 +330,44 @@ const build = <TItem, TData>(context: ListViewBuilderContext<TItem, TData>): DzL
|
|
|
320
330
|
existingByPath[path] = li
|
|
321
331
|
})
|
|
322
332
|
|
|
333
|
+
const itemKey = (item: TreeNode<TItem>) => {
|
|
334
|
+
const data = context.data?.(item) as any
|
|
335
|
+
return data?.id ?? item.path
|
|
336
|
+
}
|
|
337
|
+
|
|
323
338
|
const incomingPaths: Record<string, boolean> = {}
|
|
324
|
-
|
|
325
|
-
incomingPaths[item
|
|
326
|
-
|
|
339
|
+
const markIncoming = (item: TreeNode<TItem>) => {
|
|
340
|
+
incomingPaths[itemKey(item)] = true
|
|
341
|
+
item.children.forEach(markIncoming)
|
|
342
|
+
}
|
|
343
|
+
items.forEach(markIncoming)
|
|
344
|
+
|
|
345
|
+
const updateItem = (item: TreeNode<TItem>, parent: DzListView | DzListViewItem) => {
|
|
346
|
+
const key = itemKey(item)
|
|
347
|
+
const existing = existingByPath[key]
|
|
327
348
|
if (existing) {
|
|
328
349
|
context.text(item).forEach((text, idx) => {
|
|
350
|
+
if (idx >= context.columns.value.length) return
|
|
329
351
|
existing.setText(idx, text ?? '')
|
|
330
352
|
})
|
|
331
353
|
const data = context.data?.(item)
|
|
332
354
|
if (data) setDataItem(existing, data)
|
|
355
|
+
item.children.forEach((child) => {
|
|
356
|
+
updateItem(child, context.flat?.value === true ? listView : existing)
|
|
357
|
+
})
|
|
333
358
|
} else {
|
|
334
|
-
buildItem(item,
|
|
359
|
+
buildItem(item, parent)
|
|
335
360
|
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
|
|
361
|
+
}
|
|
362
|
+
items.forEach((item) => updateItem(item, listView))
|
|
363
|
+
|
|
364
|
+
listView.getItems(DzListView.All).sort((left, right) => {
|
|
365
|
+
const leftData = getDataItem<any>(left)
|
|
366
|
+
const rightData = getDataItem<any>(right)
|
|
367
|
+
const leftPath = leftData?.id ?? String(left.id)
|
|
368
|
+
const rightPath = rightData?.id ?? String(right.id)
|
|
369
|
+
return rightPath.length - leftPath.length
|
|
370
|
+
}).forEach((li) => {
|
|
339
371
|
const data = getDataItem<any>(li)
|
|
340
372
|
const path = data?.id ?? String(li.id)
|
|
341
373
|
if (!incomingPaths[path]) listView.deleteItem(li)
|
|
@@ -11,6 +11,7 @@ export class TabBuilder implements IWidgetBuilder<DzTabWidget> {
|
|
|
11
11
|
private currentChanged: (index: number) => void;
|
|
12
12
|
private height_: number | null = null;
|
|
13
13
|
private minHeight_: number | null = null;
|
|
14
|
+
private maxHeight_: number | null = null;
|
|
14
15
|
|
|
15
16
|
constructor(private layoutBuilder: LayoutBuilder, private context: TabBuilderContext) {
|
|
16
17
|
}
|
|
@@ -32,6 +33,11 @@ export class TabBuilder implements IWidgetBuilder<DzTabWidget> {
|
|
|
32
33
|
return this
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
maxHeight(value: number): this {
|
|
37
|
+
this.maxHeight_ = value
|
|
38
|
+
return this
|
|
39
|
+
}
|
|
40
|
+
|
|
35
41
|
static create(context: TabBuilderContext): TabBuilder {
|
|
36
42
|
return new TabBuilder(new LayoutBuilder(context.widgetContext), context)
|
|
37
43
|
}
|
|
@@ -65,6 +71,7 @@ export class TabBuilder implements IWidgetBuilder<DzTabWidget> {
|
|
|
65
71
|
return this.context.add((tabWidget) => {
|
|
66
72
|
if (this.height_ !== null) tabWidget.height = this.height_
|
|
67
73
|
if (this.minHeight_ !== null) tabWidget.minHeight = this.minHeight_
|
|
74
|
+
if (this.maxHeight_ !== null) tabWidget.maxHeight = this.maxHeight_
|
|
68
75
|
let tab = new DzWidget(this.context.widgetContext.dialog)
|
|
69
76
|
tabWidget.addTab(tab, this.titleText)
|
|
70
77
|
this.context.widgetContext.layout.addWidget(tabWidget)
|
|
@@ -77,6 +77,11 @@ export abstract class WidgetBuilderBase<T extends DzWidget> implements IWidgetBu
|
|
|
77
77
|
return this
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
maxHeight(value: number): this {
|
|
81
|
+
this.widget.maxHeight = value
|
|
82
|
+
return this
|
|
83
|
+
}
|
|
84
|
+
|
|
80
85
|
build(): T {
|
|
81
86
|
return this.widget
|
|
82
87
|
}
|
|
@@ -86,6 +91,7 @@ export interface IWidgetBuilder<T extends DzWidget> {
|
|
|
86
91
|
visible(value: boolean | Observable<boolean>): this
|
|
87
92
|
height(value: number): this
|
|
88
93
|
minHeight(value: number): this
|
|
94
|
+
maxHeight(value: number): this
|
|
89
95
|
build(): T
|
|
90
96
|
}
|
|
91
97
|
|
package/src/dialog/shared.ts
CHANGED
|
@@ -113,11 +113,25 @@ describe('install generator setup header', () => {
|
|
|
113
113
|
const projectDir = makeProject()
|
|
114
114
|
writeScript(projectDir, 'render-tools')
|
|
115
115
|
writePng(projectDir, 'Setup.header.png')
|
|
116
|
+
writePng(projectDir, 'Setup.tip.png')
|
|
116
117
|
writePng(projectDir, 'Setup.png')
|
|
117
118
|
|
|
118
119
|
const setup = generateSetup(projectDir)
|
|
119
120
|
|
|
120
121
|
expect(setup).toContain('"headerImagePath":"./Setup.header.png"')
|
|
122
|
+
expect(setup).not.toContain('"headerImagePath":"./Setup.tip.png"')
|
|
123
|
+
expect(setup).not.toContain('"headerImagePath":"./Setup.png"')
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('uses the setup tip image before the generic setup image', () => {
|
|
127
|
+
const projectDir = makeProject()
|
|
128
|
+
writeScript(projectDir, 'render-tools')
|
|
129
|
+
writePng(projectDir, 'Setup.tip.png')
|
|
130
|
+
writePng(projectDir, 'Setup.png')
|
|
131
|
+
|
|
132
|
+
const setup = generateSetup(projectDir)
|
|
133
|
+
|
|
134
|
+
expect(setup).toContain('"headerImagePath":"./Setup.tip.png"')
|
|
121
135
|
expect(setup).not.toContain('"headerImagePath":"./Setup.png"')
|
|
122
136
|
})
|
|
123
137
|
|