dazscript-framework 0.2.5 → 0.3.2
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/LICENSE +373 -0
- package/README.md +57 -1
- package/dist/scripts/app-data-path.js +50 -0
- package/dist/scripts/init.js +10 -0
- package/dist/scripts/install-generator.js +39 -39
- package/dist/scripts/launchers.js +1 -42
- package/package.json +3 -2
- package/src/{Install.dsa.ts → Setup.dsa.ts} +3 -3
- package/src/dialog/builders/list-view-builder.ts +1 -1
- package/src/dialog/builders/slider-builder.ts +1 -1
- package/src/helpers/action-helper.ts +16 -3
- package/src/helpers/custom-action-helper.ts +374 -83
- package/src/helpers/custom-action-installer-helper.ts +332 -0
- package/src/helpers/file-helper.ts +1 -1
- package/src/helpers/list-view-helper.ts +1 -1
- package/src/helpers/message-box-helper.ts +2 -2
- package/src/helpers/node-helper.ts +14 -14
- package/src/helpers/pane-helper.ts +5 -5
- package/src/helpers/scene-helper.ts +9 -9
- package/src/helpers/viewport-helper.ts +2 -2
- package/src/shared/set-keyboard-shortcut.ts +29 -5
- package/tsconfig.json +11 -2
- package/webpack.config.js +2 -1
- package/src/Uninstall.dsa.ts +0 -19
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { debug } from '@dsf/common/log'
|
|
2
|
+
import { CustomAction } from '@dsf/core/custom-action'
|
|
3
|
+
import { BasicDialog } from '@dsf/dialog/basic-dialog'
|
|
4
|
+
import { PopupMenuBuilder, PopupMenuItem } from '@dsf/dialog/builders/popup-menu-builder'
|
|
5
|
+
import { addToToolbar, applyCustomActionTargets, cleanupEmptyToolbar, clearToolbar, getInstalledCustomActionState, removeCustomActionTargets } from '@dsf/helpers/custom-action-helper'
|
|
6
|
+
import { checkAll, getDataItem } from '@dsf/helpers/list-view-helper'
|
|
7
|
+
import { normalizeShortcut, setActionShortcut } from '@dsf/helpers/action-helper'
|
|
8
|
+
import { progress } from '@dsf/helpers/progress-helper'
|
|
9
|
+
import { Observable } from '@dsf/lib/observable'
|
|
10
|
+
import CustomSet from '@dsf/lib/set'
|
|
11
|
+
import { TreeNode } from '@dsf/lib/tree-node'
|
|
12
|
+
import { promptKeyboardShortcut } from '@dsf/shared/set-keyboard-shortcut'
|
|
13
|
+
|
|
14
|
+
type InstallerEntry = {
|
|
15
|
+
action: CustomAction
|
|
16
|
+
supportsMenu: boolean
|
|
17
|
+
supportsToolbar: boolean
|
|
18
|
+
selected: boolean
|
|
19
|
+
installedMenu: boolean
|
|
20
|
+
installedToolbar: boolean
|
|
21
|
+
installedShortcut: string
|
|
22
|
+
defaultShortcut: string
|
|
23
|
+
effectiveShortcut: string
|
|
24
|
+
isShortcutOverridden: boolean
|
|
25
|
+
installedActionName: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type SetupDialogOptions = {
|
|
29
|
+
settingsPath: string
|
|
30
|
+
bundleName?: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const OVERRIDE_MARKER = '[ovr]'
|
|
34
|
+
|
|
35
|
+
const toKey = (action: CustomAction): string => String(action.filePath ?? action.text ?? '')
|
|
36
|
+
|
|
37
|
+
const toTreeNode = (entry: InstallerEntry): TreeNode<InstallerEntry> =>
|
|
38
|
+
new TreeNode(String(entry.action.text), toKey(entry.action), entry)
|
|
39
|
+
|
|
40
|
+
const getDisplayedToolbar = (entry: InstallerEntry): string => String(entry.action.toolbar ?? '')
|
|
41
|
+
|
|
42
|
+
const getSetupDialogOptions = (options: string | SetupDialogOptions): SetupDialogOptions =>
|
|
43
|
+
typeof options === 'string'
|
|
44
|
+
? { settingsPath: options }
|
|
45
|
+
: options
|
|
46
|
+
|
|
47
|
+
const getDisplayedShortcut = (entry: InstallerEntry): string => {
|
|
48
|
+
if (!entry.isShortcutOverridden) return entry.effectiveShortcut
|
|
49
|
+
return entry.effectiveShortcut
|
|
50
|
+
? `${entry.effectiveShortcut} ${OVERRIDE_MARKER}`
|
|
51
|
+
: OVERRIDE_MARKER
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const updateOverrideState = (entry: InstallerEntry) => {
|
|
55
|
+
entry.isShortcutOverridden = Boolean(entry.installedActionName) &&
|
|
56
|
+
normalizeShortcut(entry.installedShortcut) !== normalizeShortcut(entry.defaultShortcut)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const buildEntries = (actions: CustomAction[]): InstallerEntry[] => {
|
|
60
|
+
return actions
|
|
61
|
+
.filter((action) => Boolean(action.menuPath) || Boolean(action.toolbar))
|
|
62
|
+
.map((action) => {
|
|
63
|
+
const installed = getInstalledCustomActionState(action)
|
|
64
|
+
const supportsMenu = Boolean(action.menuPath)
|
|
65
|
+
const supportsToolbar = Boolean(action.toolbar)
|
|
66
|
+
const hasInstalledTargets = installed.installedMenu || installed.installedToolbar
|
|
67
|
+
const defaultShortcut = normalizeShortcut(String(action.shortcut ?? ''))
|
|
68
|
+
const installedShortcut = normalizeShortcut(String(installed.customAction?.shortcut ?? ''))
|
|
69
|
+
const entry: InstallerEntry = {
|
|
70
|
+
action: { ...action },
|
|
71
|
+
supportsMenu,
|
|
72
|
+
supportsToolbar,
|
|
73
|
+
selected: hasInstalledTargets,
|
|
74
|
+
installedMenu: installed.installedMenu,
|
|
75
|
+
installedToolbar: installed.installedToolbar,
|
|
76
|
+
installedShortcut,
|
|
77
|
+
defaultShortcut,
|
|
78
|
+
effectiveShortcut: installed.customAction?.name ? installedShortcut : defaultShortcut,
|
|
79
|
+
isShortcutOverridden: false,
|
|
80
|
+
installedActionName: String(installed.customAction?.name ?? '')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
updateOverrideState(entry)
|
|
84
|
+
return entry
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
class InstallerSelectionDialog extends BasicDialog {
|
|
89
|
+
private readonly keywords$ = new Observable('')
|
|
90
|
+
private readonly items$: Observable<TreeNode<InstallerEntry>[]>
|
|
91
|
+
private readonly refreshListEvent$ = new Observable<void>()
|
|
92
|
+
private listView: DzListView | null = null
|
|
93
|
+
|
|
94
|
+
constructor(private readonly entries: InstallerEntry[], bundleName?: string) {
|
|
95
|
+
super(bundleName ? `${bundleName} Setup` : 'Setup Scripts', 'dsfSetupScripts')
|
|
96
|
+
this.items$ = new Observable(entries.map(toTreeNode))
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
protected build(): void {
|
|
100
|
+
this.builder.options({ resizable: true, width: 1100, height: 700 })
|
|
101
|
+
this.dialog.setAcceptButtonText('Apply')
|
|
102
|
+
this.dialog.setCancelButtonText('Cancel')
|
|
103
|
+
|
|
104
|
+
const add = this.add
|
|
105
|
+
add.label('Choose which scripts to install, then use the columns to review their shortcut, menu, and toolbar targets.')
|
|
106
|
+
.wordWrap()
|
|
107
|
+
.build()
|
|
108
|
+
add.group('Search').horizontal().build(() => {
|
|
109
|
+
add.edit()
|
|
110
|
+
.value(this.keywords$)
|
|
111
|
+
.focus()
|
|
112
|
+
.placeholder('Search actions...')
|
|
113
|
+
.toolTip('Search by action name, description, path, shortcut, or available targets.')
|
|
114
|
+
add.button('Select All').clicked(() => this.setVisibleSelections(true))
|
|
115
|
+
add.button('Deselect All').clicked(() => this.setVisibleSelections(false))
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
add.group('Scripts')
|
|
119
|
+
.style({ flat: true })
|
|
120
|
+
.build(() => {
|
|
121
|
+
add.list.view<InstallerEntry, InstallerEntry>()
|
|
122
|
+
.sorting(true)
|
|
123
|
+
.sortOnBuild(true)
|
|
124
|
+
.refresh(this.refreshListEvent$)
|
|
125
|
+
.row((item, parent, id) => {
|
|
126
|
+
const listItem = new DzCheckListItem(parent, DzCheckListItem.CheckBox, id)
|
|
127
|
+
listItem.on = item.value.selected
|
|
128
|
+
listItem.setText(0, '')
|
|
129
|
+
return listItem
|
|
130
|
+
})
|
|
131
|
+
.contextMenu((listView, item) => this.buildContextMenu(listView, item))
|
|
132
|
+
.items(this.items$)
|
|
133
|
+
.columns(['Install', 'Action', 'Shortcut', 'Description', 'Menu', 'Toolbar'], (index, width) => {
|
|
134
|
+
if (index === 0) return Math.max(width, 70)
|
|
135
|
+
if (index === 1) return Math.max(width * 1.6, 220)
|
|
136
|
+
if (index === 2) return Math.max(width, 140)
|
|
137
|
+
if (index === 3) return Math.max(width * 1.8, 240)
|
|
138
|
+
if (index === 4) return Math.max(width * 2.5, 320)
|
|
139
|
+
if (index === 5) return Math.max(width * 1.2, 140)
|
|
140
|
+
return width
|
|
141
|
+
})
|
|
142
|
+
.text((item) => [
|
|
143
|
+
'',
|
|
144
|
+
String(item.value.action.text ?? ''),
|
|
145
|
+
getDisplayedShortcut(item.value),
|
|
146
|
+
String(item.value.action.description ?? ''),
|
|
147
|
+
String(item.value.action.menuPath ?? ''),
|
|
148
|
+
getDisplayedToolbar(item.value),
|
|
149
|
+
])
|
|
150
|
+
.data((item) => item.value)
|
|
151
|
+
.filter({
|
|
152
|
+
keywords: this.keywords$,
|
|
153
|
+
field: (listItem) => [
|
|
154
|
+
listItem.text(1),
|
|
155
|
+
listItem.text(2),
|
|
156
|
+
listItem.text(3),
|
|
157
|
+
listItem.text(4),
|
|
158
|
+
listItem.text(5),
|
|
159
|
+
].join(' ')
|
|
160
|
+
})
|
|
161
|
+
.build((listView) => {
|
|
162
|
+
this.listView = listView
|
|
163
|
+
listView.allColumnsShowFocus = true
|
|
164
|
+
})
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
getSelections(): InstallerEntry[] {
|
|
169
|
+
this.syncSelectionsFromListView()
|
|
170
|
+
return this.entries
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private buildContextMenu(listView: DzListView, listItem: DzListViewItem | null): DzPopupMenu {
|
|
174
|
+
const entry = listItem ? getDataItem<InstallerEntry>(listItem) : null
|
|
175
|
+
const canSetShortcut = Boolean(entry)
|
|
176
|
+
const canResetShortcut = Boolean(entry?.installedActionName && entry?.isShortcutOverridden)
|
|
177
|
+
|
|
178
|
+
const items = [
|
|
179
|
+
{
|
|
180
|
+
text: 'Set Shortcut',
|
|
181
|
+
activated: () => {
|
|
182
|
+
if (!entry) return
|
|
183
|
+
this.setShortcut(entry)
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
text: 'Reset Default Shortcut',
|
|
188
|
+
activated: () => {
|
|
189
|
+
if (!entry) return
|
|
190
|
+
this.resetDefaultShortcut(entry)
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
{},
|
|
194
|
+
{ text: 'Check All', activated: () => this.checkVisible(listView, true) },
|
|
195
|
+
{ text: 'Uncheck All', activated: () => this.checkVisible(listView, false) }
|
|
196
|
+
] as PopupMenuItem[]
|
|
197
|
+
|
|
198
|
+
return new PopupMenuBuilder(this.builder.context).items(...items).build((menu) => {
|
|
199
|
+
menu.setItemEnabled(0, canSetShortcut)
|
|
200
|
+
menu.setItemEnabled(1, canResetShortcut)
|
|
201
|
+
})
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private setShortcut(entry: InstallerEntry) {
|
|
205
|
+
const actionName = entry.installedActionName || toKey(entry.action)
|
|
206
|
+
const actionLabel = String(entry.action.text ?? entry.action.filePath ?? actionName)
|
|
207
|
+
const shortcut = promptKeyboardShortcut(actionLabel, actionName, entry.effectiveShortcut)
|
|
208
|
+
if (shortcut == null) return
|
|
209
|
+
|
|
210
|
+
if (entry.installedActionName) {
|
|
211
|
+
setActionShortcut(entry.installedActionName, shortcut)
|
|
212
|
+
entry.installedShortcut = normalizeShortcut(shortcut)
|
|
213
|
+
entry.effectiveShortcut = entry.installedShortcut
|
|
214
|
+
updateOverrideState(entry)
|
|
215
|
+
} else {
|
|
216
|
+
entry.effectiveShortcut = normalizeShortcut(shortcut)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
this.refreshListEvent$.trigger()
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
private resetDefaultShortcut(entry: InstallerEntry) {
|
|
223
|
+
if (!entry.installedActionName || !entry.isShortcutOverridden) return
|
|
224
|
+
|
|
225
|
+
setActionShortcut(entry.installedActionName, entry.defaultShortcut)
|
|
226
|
+
entry.installedShortcut = normalizeShortcut(entry.defaultShortcut)
|
|
227
|
+
entry.effectiveShortcut = entry.installedShortcut
|
|
228
|
+
updateOverrideState(entry)
|
|
229
|
+
this.refreshListEvent$.trigger()
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private checkVisible(listView: DzListView, onOff: boolean) {
|
|
233
|
+
if (!this.keywords$.value?.trim()) {
|
|
234
|
+
checkAll(listView, onOff)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
listView.getItems(DzListView.All).forEach((item) => {
|
|
238
|
+
if (!item.visible || !(item as any).inherits('DzCheckListItem')) return
|
|
239
|
+
|
|
240
|
+
const checkbox = item as DzCheckListItem
|
|
241
|
+
checkbox.on = onOff
|
|
242
|
+
|
|
243
|
+
const data = getDataItem<InstallerEntry>(item)
|
|
244
|
+
if (!data) return
|
|
245
|
+
data.selected = onOff
|
|
246
|
+
})
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private setVisibleSelections(onOff: boolean) {
|
|
250
|
+
if (!this.listView) return
|
|
251
|
+
this.checkVisible(this.listView, onOff)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
private syncSelectionsFromListView() {
|
|
255
|
+
if (!this.listView) return
|
|
256
|
+
|
|
257
|
+
const selectionByKey: Record<string, boolean> = {}
|
|
258
|
+
|
|
259
|
+
this.listView.getItems(DzListView.All).forEach((item) => {
|
|
260
|
+
if (!(item as any).inherits('DzCheckListItem')) return
|
|
261
|
+
|
|
262
|
+
const data = getDataItem<InstallerEntry>(item)
|
|
263
|
+
if (!data) return
|
|
264
|
+
|
|
265
|
+
const checkbox = item as DzCheckListItem
|
|
266
|
+
const selected = !(checkbox.state === 0 && !checkbox.on)
|
|
267
|
+
selectionByKey[toKey(data.action)] = selected
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
this.entries.forEach((entry) => {
|
|
271
|
+
const key = toKey(entry.action)
|
|
272
|
+
if (Object.prototype.hasOwnProperty.call(selectionByKey, key)) {
|
|
273
|
+
entry.selected = selectionByKey[key]
|
|
274
|
+
}
|
|
275
|
+
})
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const runDialog = (actions: CustomAction[], options: SetupDialogOptions): InstallerEntry[] | null => {
|
|
280
|
+
const entries = buildEntries(actions)
|
|
281
|
+
const dialog = new InstallerSelectionDialog(entries, options.bundleName)
|
|
282
|
+
return dialog.ok() ? dialog.getSelections() : null
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export const showSetupCustomActionsDialog = (actions: CustomAction[], options: string | SetupDialogOptions) => {
|
|
286
|
+
const settings = getSetupDialogOptions(options)
|
|
287
|
+
const selections = runDialog(actions, settings)
|
|
288
|
+
if (!selections) return
|
|
289
|
+
|
|
290
|
+
debug(`[Setup] applying ${selections.filter(selection => selection.selected).length}/${selections.length} selected actions`)
|
|
291
|
+
|
|
292
|
+
const removedToolbarNames = new CustomSet<string>()
|
|
293
|
+
|
|
294
|
+
progress('Setting Up Scripts', selections, (selection) => {
|
|
295
|
+
debug(`[Setup] ${selection.selected ? 'install' : 'remove'} "${selection.action.text}"`)
|
|
296
|
+
|
|
297
|
+
if (selection.selected) {
|
|
298
|
+
applyCustomActionTargets({
|
|
299
|
+
...selection.action,
|
|
300
|
+
shortcut: selection.effectiveShortcut
|
|
301
|
+
}, {
|
|
302
|
+
menu: selection.supportsMenu,
|
|
303
|
+
toolbar: selection.supportsToolbar
|
|
304
|
+
})
|
|
305
|
+
return
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (selection.supportsToolbar && selection.action.toolbar) {
|
|
309
|
+
removedToolbarNames.add(selection.action.toolbar)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
removeCustomActionTargets(selection.action, {
|
|
313
|
+
menu: selection.supportsMenu,
|
|
314
|
+
toolbar: selection.supportsToolbar
|
|
315
|
+
})
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
removedToolbarNames.forEach((toolbarName) => {
|
|
319
|
+
clearToolbar(toolbarName)
|
|
320
|
+
|
|
321
|
+
const stillSelected = selections.filter(s => s.selected && s.supportsToolbar && s.action.toolbar === toolbarName)
|
|
322
|
+
stillSelected.forEach((s) => addToToolbar({
|
|
323
|
+
...s.action,
|
|
324
|
+
shortcut: s.effectiveShortcut
|
|
325
|
+
}))
|
|
326
|
+
debug(`[Setup] Toolbar ${toolbarName} rebuilt with ${stillSelected.length} remaining actions`)
|
|
327
|
+
|
|
328
|
+
if (stillSelected.length === 0) {
|
|
329
|
+
cleanupEmptyToolbar(toolbarName)
|
|
330
|
+
}
|
|
331
|
+
})
|
|
332
|
+
}
|
|
@@ -13,7 +13,7 @@ export const setDataItem = (listItem: DzListViewItem, data: any) => {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export const getDataItem = <T>(listItem: DzListViewItem): T | null => {
|
|
16
|
-
return listItem?.getDataItem('data') ?? null
|
|
16
|
+
return (listItem?.getDataItem('data') ?? null) as T | null
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const filter = (listView: DzListView, filterOn: (viewItem: DzListViewItem) => string, keywords: string, options?: { selectOnFilter?: boolean, filters?: (viewItem: DzListViewItem) => boolean }) => {
|
|
@@ -10,7 +10,7 @@ export const error = (message: string, writeLog: boolean = true) => {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export const warning = (message: string) => {
|
|
13
|
-
MessageBox.warning(message, "Warning", "Ok")
|
|
13
|
+
MessageBox.warning(message, "Warning", "Ok", "")
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export const confirm = (message?: string): { ok: boolean, cancel: boolean } => {
|
|
@@ -20,7 +20,7 @@ export const confirm = (message?: string): { ok: boolean, cancel: boolean } => {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export const prompt = (text: string, title: string, button0: string, button1?: string): { cancel: boolean, selection: number } => {
|
|
23
|
-
const response = MessageBox.question(text, title, button0, button1, "Cancel")
|
|
23
|
+
const response = MessageBox.question(text, title, button0, button1 ?? "", "Cancel")
|
|
24
24
|
|
|
25
25
|
return { cancel: response === 0, selection: response };
|
|
26
26
|
}
|
|
@@ -26,7 +26,7 @@ export const isFigure = (node: DzNode): boolean => {
|
|
|
26
26
|
* @param node
|
|
27
27
|
* @returns The root of the node, if the node is part of a figure, return the figure (skeleton) otherwise return the node itself
|
|
28
28
|
*/
|
|
29
|
-
export const getRoot = (node: DzNode): DzNode => {
|
|
29
|
+
export const getRoot = (node: DzNode | null): DzNode | null => {
|
|
30
30
|
if (node && node.className() === "DzBone" && node.getSkeleton)
|
|
31
31
|
return node.getSkeleton();
|
|
32
32
|
else
|
|
@@ -44,8 +44,8 @@ export const getFigure = (node: DzNode): DzSkeleton | null => {
|
|
|
44
44
|
* @returns The first property with the given name or internal name, or NULL.
|
|
45
45
|
*/
|
|
46
46
|
export const findProperty = <T extends DzProperty = DzProperty>(node: DzNode, name: string): T | null => {
|
|
47
|
-
return sceneHelper.findPropertyOnNode(name, node) as T
|
|
48
|
-
?? sceneHelper.findPropertyOnNodeByInternalName(name, node) as T
|
|
47
|
+
return sceneHelper.findPropertyOnNode(name, node) as unknown as T
|
|
48
|
+
?? sceneHelper.findPropertyOnNodeByInternalName(name, node) as unknown as T
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/**
|
|
@@ -90,7 +90,7 @@ export const isChildOf = (node: DzNode, figure: DzNode): boolean => {
|
|
|
90
90
|
* @returns the fitting target (skeleton) of the node or null
|
|
91
91
|
*/
|
|
92
92
|
export const getFittingTarget = (node: DzNode): DzSkeleton | null => {
|
|
93
|
-
return getRoot(node)
|
|
93
|
+
return getRoot(node)?.getSkeleton()?.getFollowTarget() ?? null
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/**
|
|
@@ -99,7 +99,7 @@ export const getFittingTarget = (node: DzNode): DzSkeleton | null => {
|
|
|
99
99
|
* @param target if specified, check if the node is fitted to the target node, otherwise check if the node is fitted to any other node
|
|
100
100
|
* @returns true if the node is fitted to another node
|
|
101
101
|
*/
|
|
102
|
-
export const isFitting = (figure: DzSkeleton, target?: DzNode): boolean => {
|
|
102
|
+
export const isFitting = (figure: DzSkeleton | null, target?: DzNode): boolean => {
|
|
103
103
|
return target
|
|
104
104
|
? figure?.getFollowTarget() == target
|
|
105
105
|
: figure?.getFollowTarget() != null
|
|
@@ -140,12 +140,12 @@ export const getTransforms = (node: DzNode, include: { rotations?: boolean, tran
|
|
|
140
140
|
export const getRotations = (node: DzNode, includeExtraRotations: boolean = true): DzFloatProperty[] => {
|
|
141
141
|
if (!node) return [];
|
|
142
142
|
|
|
143
|
-
const xRotate = sceneHelper.findPropertyOnNode('XRotate', node) as DzFloatProperty;
|
|
144
|
-
const xRotate2 = sceneHelper.findPropertyOnNode('XRotate2', node) as DzFloatProperty;
|
|
145
|
-
const yRotate = sceneHelper.findPropertyOnNode('YRotate', node) as DzFloatProperty;
|
|
146
|
-
const yRotate2 = sceneHelper.findPropertyOnNode('YRotate2', node) as DzFloatProperty;
|
|
147
|
-
const zRotate = sceneHelper.findPropertyOnNode('ZRotate', node) as DzFloatProperty;
|
|
148
|
-
const zRotate2 = sceneHelper.findPropertyOnNode('ZRotate2', node) as DzFloatProperty;
|
|
143
|
+
const xRotate = sceneHelper.findPropertyOnNode('XRotate', node) as unknown as DzFloatProperty;
|
|
144
|
+
const xRotate2 = sceneHelper.findPropertyOnNode('XRotate2', node) as unknown as DzFloatProperty;
|
|
145
|
+
const yRotate = sceneHelper.findPropertyOnNode('YRotate', node) as unknown as DzFloatProperty;
|
|
146
|
+
const yRotate2 = sceneHelper.findPropertyOnNode('YRotate2', node) as unknown as DzFloatProperty;
|
|
147
|
+
const zRotate = sceneHelper.findPropertyOnNode('ZRotate', node) as unknown as DzFloatProperty;
|
|
148
|
+
const zRotate2 = sceneHelper.findPropertyOnNode('ZRotate2', node) as unknown as DzFloatProperty;
|
|
149
149
|
|
|
150
150
|
if (includeExtraRotations) {
|
|
151
151
|
return [xRotate, xRotate2, yRotate, yRotate2, zRotate, zRotate2].filter(Boolean);
|
|
@@ -162,7 +162,7 @@ export const getRotations = (node: DzNode, includeExtraRotations: boolean = true
|
|
|
162
162
|
export const getRotationsForAxis = (node: DzNode, axis: 'x' | 'y' | 'z'): DzFloatProperty => {
|
|
163
163
|
debug(`Getting rotation property for axis: ${axis} on node: ${node.getName()} - isBone: ${isBone(node)}`);
|
|
164
164
|
|
|
165
|
-
let prop: DzFloatProperty |
|
|
165
|
+
let prop: DzFloatProperty | null = null;
|
|
166
166
|
|
|
167
167
|
if (isBone(node)) {
|
|
168
168
|
prop = getNamedRotationForAxis(node, axis);
|
|
@@ -175,7 +175,7 @@ export const getRotationsForAxis = (node: DzNode, axis: 'x' | 'y' | 'z'): DzFloa
|
|
|
175
175
|
prop = find(
|
|
176
176
|
getRotations(node, false),
|
|
177
177
|
(rotation) => rotation.getName().valueOf()[0].toLowerCase() === axis
|
|
178
|
-
) as DzFloatProperty;
|
|
178
|
+
) as unknown as DzFloatProperty;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
return prop;
|
|
@@ -185,7 +185,7 @@ export const getNamedRotations = (node: DzNode): DzFloatProperty[] => {
|
|
|
185
185
|
const rotations: DzFloatProperty[] = []
|
|
186
186
|
|
|
187
187
|
for (let namedAxis in NamedAxis) {
|
|
188
|
-
let property = sceneHelper.findPropertyOnNodeByLabel(NamedAxis[namedAxis], node) as DzFloatProperty
|
|
188
|
+
let property = sceneHelper.findPropertyOnNodeByLabel(NamedAxis[namedAxis], node) as unknown as DzFloatProperty
|
|
189
189
|
if (property) rotations.push(property)
|
|
190
190
|
}
|
|
191
191
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { mainWindow } from '@dsf/core/global'
|
|
2
2
|
|
|
3
3
|
const findPane = <T extends DzPane>(className: string): T => {
|
|
4
|
-
return mainWindow.getPaneMgr().findPane(className) as T
|
|
4
|
+
return mainWindow.getPaneMgr().findPane(className) as unknown as T
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export const getSmartContentPane = (): DzSmartContentPane => {
|
|
8
|
-
return
|
|
8
|
+
return findPane("DzSmartContentPane") as unknown as DzSmartContentPane
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export const getSurfacesPane = (): DzSurfacesPane => {
|
|
@@ -20,10 +20,10 @@ export const getParametersPane = (): DzParametersPane => {
|
|
|
20
20
|
return findPane<DzParametersPane>("DzParametersPane")
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export const getParametersPaneNodeEditor = (): DzPropertySideNavHierarchy => {
|
|
23
|
+
export const getParametersPaneNodeEditor = (): DzPropertySideNavHierarchy | null => {
|
|
24
24
|
return findPane<DzParametersPane>("DzParametersPane")?.getNodeEditor() ?? null
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export const getPaneNodeEditor = (pane: DzAbstractNodeEditorPane): DzPropertySideNavHierarchy => {
|
|
27
|
+
export const getPaneNodeEditor = (pane: DzAbstractNodeEditorPane): DzPropertySideNavHierarchy | null => {
|
|
28
28
|
return pane.getNodeEditor ? pane.getNodeEditor() : null
|
|
29
|
-
}
|
|
29
|
+
}
|
|
@@ -4,9 +4,9 @@ import { getRoot, isFigure } from './node-helper'
|
|
|
4
4
|
import { getPaneNodeEditor, getParametersPane, getParametersPaneNodeEditor } from './pane-helper'
|
|
5
5
|
|
|
6
6
|
export const getSelectedOf = <T>(typeName: string): DzNode[] => {
|
|
7
|
-
let nodes = []
|
|
7
|
+
let nodes: DzNode[] = []
|
|
8
8
|
for (let node of scene.getSelectedNodeList()) {
|
|
9
|
-
if (!node.inherits(typeName)) continue
|
|
9
|
+
if (!node || !node.inherits(typeName)) continue
|
|
10
10
|
nodes.push(node)
|
|
11
11
|
}
|
|
12
12
|
return nodes
|
|
@@ -45,7 +45,7 @@ export const getFigures = (): DzSkeleton[] => {
|
|
|
45
45
|
* @returns the figure skeleton, or null if there is no current selection or the selected node is not part of a figure
|
|
46
46
|
*/
|
|
47
47
|
export const getSelectedFigure = (): DzSkeleton | null => {
|
|
48
|
-
return getSelectedNode()?.getSkeleton?.()
|
|
48
|
+
return getSelectedNode()?.getSkeleton?.() ?? null
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/**
|
|
@@ -66,12 +66,12 @@ export const getSelectedNodes = (): DzNode[] => {
|
|
|
66
66
|
return scene.getSelectedNodeList()
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
export const getSelectedRoot = (): DzNode => {
|
|
69
|
+
export const getSelectedRoot = (): DzNode | null => {
|
|
70
70
|
return getRoot(getSelectedNode())
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
export const getSelectedRoots = (): DzNode[] => {
|
|
74
|
-
return distinct(getSelectedNodes().map(n => getRoot(n)))
|
|
74
|
+
return distinct(getSelectedNodes().map(n => getRoot(n))).filter(n => n !== null) as DzNode[]
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
export const getSelectedProperties = (): DzProperty[] => {
|
|
@@ -79,7 +79,7 @@ export const getSelectedProperties = (): DzProperty[] => {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
export const getSelectedNumericProperties = (): (DzFloatProperty | DzIntProperty | DzBoolProperty)[] => {
|
|
82
|
-
return getParametersPane()?.getNodeEditor()?.getPropertySelections(true).map(p => p as DzFloatProperty | DzIntProperty | DzBoolProperty) ?? []
|
|
82
|
+
return getParametersPane()?.getNodeEditor()?.getPropertySelections(true).map(p => p as unknown as DzFloatProperty | DzIntProperty | DzBoolProperty) ?? []
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
export const getSelectedPropertiesOfType = <TProperty extends DzProperty>(type: string): TProperty[] => {
|
|
@@ -125,7 +125,7 @@ export const getCurrentFrame = (): number => {
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
export const getEndFrame = (): number => {
|
|
128
|
-
return scene.getAnimRange().end / scene.getTimeStep().valueOf()
|
|
128
|
+
return scene.getAnimRange().end.valueOf() / scene.getTimeStep().valueOf()
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
/**
|
|
@@ -133,7 +133,7 @@ export const getEndFrame = (): number => {
|
|
|
133
133
|
* @returns the last frame number (0-based)
|
|
134
134
|
*/
|
|
135
135
|
export const getLastFrame = (): number => {
|
|
136
|
-
return scene.getAnimRange().end / scene.getTimeStep().valueOf()
|
|
136
|
+
return scene.getAnimRange().end.valueOf() / scene.getTimeStep().valueOf()
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
export const timeToFrame = (time: DzTime): number => {
|
|
@@ -142,4 +142,4 @@ export const timeToFrame = (time: DzTime): number => {
|
|
|
142
142
|
|
|
143
143
|
export const frameToTime = (frame: number): DzTime => {
|
|
144
144
|
return new DzTime(scene.getTimeStep().valueOf() * frame)
|
|
145
|
-
}
|
|
145
|
+
}
|
|
@@ -2,7 +2,7 @@ import { mainWindow } from '@dsf/core/global'
|
|
|
2
2
|
|
|
3
3
|
export const selectUniversalRotateTool = (coordinateSpace?: number): DzUniversalRotateTool => {
|
|
4
4
|
const viewportMgr = mainWindow.getViewportMgr()
|
|
5
|
-
const tool = viewportMgr.findTool('DzUniversalRotateTool') as DzUniversalRotateTool
|
|
5
|
+
const tool = viewportMgr.findTool('DzUniversalRotateTool') as unknown as DzUniversalRotateTool
|
|
6
6
|
viewportMgr.setActiveTool(tool)
|
|
7
7
|
if (coordinateSpace) tool.setCoordinateSpace(coordinateSpace)
|
|
8
8
|
return tool
|
|
@@ -19,4 +19,4 @@ export const getAuxViewport = (): DzViewport | null => {
|
|
|
19
19
|
if (viewport.name === 'AuxViewportView') return viewport
|
|
20
20
|
}
|
|
21
21
|
return null
|
|
22
|
-
}
|
|
22
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BasicDialog } from '@dsf/dialog/basic-dialog';
|
|
2
2
|
import { debug } from '@dsf/common/log';
|
|
3
|
-
import { findAction, findActionsForShortcut, setActionShortcut } from '@dsf/helpers/action-helper';
|
|
3
|
+
import { findAction, findActionsForShortcut, getActionShortcut, normalizeShortcut, setActionShortcut } from '@dsf/helpers/action-helper';
|
|
4
4
|
import { contains } from '@dsf/helpers/array-helper';
|
|
5
5
|
import { confirm } from '@dsf/helpers/message-box-helper';
|
|
6
6
|
import { Observable } from '@dsf/lib/observable';
|
|
@@ -69,6 +69,8 @@ class KeyboardShortcutDialog extends BasicDialog {
|
|
|
69
69
|
this.dialog.setAcceptButtonEnabled(Boolean(text))
|
|
70
70
|
})
|
|
71
71
|
|
|
72
|
+
this.loadInitialShortcut(model.shortcut.value)
|
|
73
|
+
|
|
72
74
|
add.group('Assign Keyboard Shortcut').build((layout) => {
|
|
73
75
|
layout.spacing = 5
|
|
74
76
|
add.edit().value(model.actionLabel).readOnly()
|
|
@@ -117,17 +119,32 @@ class KeyboardShortcutDialog extends BasicDialog {
|
|
|
117
119
|
|
|
118
120
|
return `Already assigned to: ${conflicts.map(action => action.text || action.name).join(', ')}`
|
|
119
121
|
}
|
|
122
|
+
|
|
123
|
+
private loadInitialShortcut(shortcut: string): void {
|
|
124
|
+
const normalized = normalizeShortcut(shortcut)
|
|
125
|
+
if (!normalized) return
|
|
126
|
+
|
|
127
|
+
const parts = normalized.split('+').map(part => part.trim()).filter(Boolean)
|
|
128
|
+
const key = parts.pop() ?? ''
|
|
129
|
+
|
|
130
|
+
this.model.control.value = parts.indexOf('Ctrl') >= 0
|
|
131
|
+
this.model.alt.value = parts.indexOf('Alt') >= 0
|
|
132
|
+
this.model.shift.value = parts.indexOf('Shift') >= 0
|
|
133
|
+
this.model.windows.value = parts.indexOf('Win') >= 0
|
|
134
|
+
this.key.value = key
|
|
135
|
+
}
|
|
120
136
|
}
|
|
121
137
|
|
|
122
|
-
export const
|
|
138
|
+
export const promptKeyboardShortcut = (actionLabel: string, actionName: string, initialShortcut?: string): string | null => {
|
|
123
139
|
let model = new KeyboardShortcutModel()
|
|
124
140
|
model.actionLabel = actionLabel
|
|
125
141
|
model.actionName = actionName
|
|
142
|
+
model.shortcut.value = normalizeShortcut(initialShortcut ?? getActionShortcut(actionName) ?? '')
|
|
126
143
|
|
|
127
144
|
let dialog = new KeyboardShortcutDialog(model)
|
|
128
145
|
let result = dialog.run()
|
|
129
146
|
|
|
130
|
-
if (!result || !model.shortcut) return
|
|
147
|
+
if (!result || !model.shortcut) return null
|
|
131
148
|
|
|
132
149
|
const conflicts = findActionsForShortcut(model.shortcut.value)
|
|
133
150
|
.filter(action => action && action.name !== actionName)
|
|
@@ -139,8 +156,15 @@ export const setKeyboardShortcut = (actionLabel: string, actionName: string) =>
|
|
|
139
156
|
if (conflicts.length > 0) {
|
|
140
157
|
const conflictText = conflicts.map(action => action.text || action.name).join(', ')
|
|
141
158
|
const response = confirm(`"${model.shortcut.value}" is already assigned to: ${conflictText}\n\nReplace it?`)
|
|
142
|
-
if (!response.ok) return
|
|
159
|
+
if (!response.ok) return null
|
|
143
160
|
}
|
|
144
161
|
|
|
145
|
-
|
|
162
|
+
return model.shortcut.value
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export const setKeyboardShortcut = (actionLabel: string, actionName: string, initialShortcut?: string) => {
|
|
166
|
+
const shortcut = promptKeyboardShortcut(actionLabel, actionName, initialShortcut)
|
|
167
|
+
if (!shortcut) return
|
|
168
|
+
|
|
169
|
+
setActionShortcut(actionName, shortcut)
|
|
146
170
|
}
|
package/tsconfig.json
CHANGED
|
@@ -32,7 +32,11 @@
|
|
|
32
32
|
"baseUrl": "./" /* Specify the base directory to resolve non-relative module names. */,
|
|
33
33
|
"paths": {
|
|
34
34
|
"shared/*": ["src/shared/*"],
|
|
35
|
-
"@dst/*": [
|
|
35
|
+
"@dst/*": [
|
|
36
|
+
"node_modules/dazscript-types/src/types/*",
|
|
37
|
+
"../node_modules/dazscript-types/src/types/*",
|
|
38
|
+
"../script-types/src/types/*"
|
|
39
|
+
],
|
|
36
40
|
"@dsf/*": ["src/*"]
|
|
37
41
|
},
|
|
38
42
|
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
|
@@ -112,5 +116,10 @@
|
|
|
112
116
|
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
|
113
117
|
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
|
114
118
|
},
|
|
115
|
-
"include": [
|
|
119
|
+
"include": [
|
|
120
|
+
"node_modules/dazscript-types/src/types/**/*",
|
|
121
|
+
"../node_modules/dazscript-types/src/types/**/*",
|
|
122
|
+
"../script-types/src/types/**/*",
|
|
123
|
+
"src/**/*"
|
|
124
|
+
]
|
|
116
125
|
}
|
package/webpack.config.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const glob = require('glob');
|
|
3
3
|
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
|
4
|
-
const { createActionLaunchers
|
|
4
|
+
const { createActionLaunchers } = require('./dist/scripts/launchers');
|
|
5
|
+
const { validateAppDataPath } = require('./dist/scripts/app-data-path');
|
|
5
6
|
const { loadConfig } = require('./dist/scripts/config-loader');
|
|
6
7
|
|
|
7
8
|
class ActionLauncherPlugin {
|