dazscript-framework 1.0.23 → 1.0.25

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dazscript-framework",
3
- "version": "1.0.23",
3
+ "version": "1.0.25",
4
4
  "author": "Freddy Diaz",
5
5
  "license": "MPL-2.0",
6
6
  "description": "",
@@ -24,10 +24,7 @@ export abstract class BasicDialog {
24
24
  run(): boolean {
25
25
  this.init()
26
26
  this.builder.restoreObjectName()
27
- this.builder.traceGeometry('beforeExec')
28
- let result = this.dialog.exec()
29
- this.builder.traceGeometry('afterExec', { result })
30
- return result
27
+ return this.dialog.exec()
31
28
  }
32
29
 
33
30
  ok(): boolean {
@@ -1,6 +1,5 @@
1
1
  import { DialogProperties } from '../shared';
2
2
  import { WidgetBuilderContext, WidgetsBuilder } from './widgets-builder';
3
- import { debug } from '@dsf/common/log';
4
3
 
5
4
  export class DialogBuilder extends WidgetsBuilder {
6
5
  public properties: DialogProperties
@@ -17,13 +16,10 @@ export class DialogBuilder extends WidgetsBuilder {
17
16
 
18
17
  build(setup: () => void): DzBasicDialog {
19
18
  this.init()
20
- this.traceGeometry('init')
21
19
  setup()
22
20
  this.restoreObjectName()
23
- this.traceGeometry('afterSetup')
24
21
  this.resize()
25
22
  this.restoreObjectName()
26
- this.traceGeometry('afterResize')
27
23
  return this.context.dialog
28
24
  }
29
25
 
@@ -69,64 +65,4 @@ export class DialogBuilder extends WidgetsBuilder {
69
65
  this.context.dialog.setFixedHeight(height);
70
66
  }
71
67
  }
72
-
73
- traceGeometry(phase: string, extra: { [key: string]: string | number | boolean | undefined } = {}) {
74
- let dialog = this.context.dialog
75
- let dialogWidget = dialog.getWidget()
76
- let objectName = this.read(dialogWidget, 'objectName')
77
- let caption = this.read(dialog, 'caption')
78
- let sizeHint = this.read(dialogWidget, 'minimumSizeHint')
79
- let key = objectName ?? ''
80
- let fields: { [key: string]: string | number | boolean | undefined } = {
81
- event: 'dialog.geometry',
82
- phase,
83
- title: this.title,
84
- id: this.id ?? '',
85
- caption,
86
- objectName,
87
- windowGeometryKey: key,
88
- dialogX: this.read(dialog, 'x'),
89
- dialogY: this.read(dialog, 'y'),
90
- dialogWidth: this.read(dialog, 'width'),
91
- dialogHeight: this.read(dialog, 'height'),
92
- widgetX: this.read(dialogWidget, 'x'),
93
- widgetY: this.read(dialogWidget, 'y'),
94
- widgetWidth: this.read(dialogWidget, 'width'),
95
- widgetHeight: this.read(dialogWidget, 'height'),
96
- sizeHintWidth: this.read(sizeHint, 'width'),
97
- sizeHintHeight: this.read(sizeHint, 'height'),
98
- propertyWidth: this.properties.width,
99
- propertyHeight: this.properties.height,
100
- propertyMaxWidth: this.properties.maxWidth,
101
- propertyMaxHeight: this.properties.maxHeight,
102
- resizable: this.properties.resizable === true,
103
- registrySubKey: key ? `WindowGeometries\\${key}` : ''
104
- }
105
-
106
- Object.keys(extra).forEach((name) => fields[name] = extra[name])
107
- debug(`[DialogGeometry] ${this.formatGeometryTrace(fields)}`)
108
- }
109
-
110
- private read(source: any, field: string): any {
111
- if (!source) return undefined
112
- try {
113
- return source[field]
114
- }
115
- catch (err) {
116
- return undefined
117
- }
118
- }
119
-
120
- private formatGeometryTrace(fields: { [key: string]: string | number | boolean | undefined }): string {
121
- return Object.keys(fields)
122
- .filter((name) => fields[name] !== undefined)
123
- .map((name) => `${name}=${this.formatGeometryValue(name, fields[name])}`)
124
- .join(' ')
125
- }
126
-
127
- private formatGeometryValue(name: string, value: string | number | boolean | undefined): string {
128
- if (name === 'event' || name === 'phase') return String(value)
129
- if (typeof value === 'string') return JSON.stringify(value)
130
- return String(value)
131
- }
132
68
  }
@@ -12,7 +12,7 @@ import { TreeNode } from '@dsf/lib/tree-node'
12
12
  import { promptKeyboardShortcut } from '@dsf/shared/set-keyboard-shortcut'
13
13
  import { readFromFile, saveToFile } from './file-helper'
14
14
  import { getCanonicalInstallerEntry, toActionKey } from './custom-action-installer-entries'
15
- import { canResetSetupShortcut, getDisplayedSetupShortcut, resetSetupShortcut, setSetupShortcut, updateShortcutOverrideState } from './custom-action-installer-shortcuts'
15
+ import { canResetSetupShortcut, getDisplayedSetupShortcut, resetSetupShortcut, resetSetupShortcuts, setSetupShortcut, updateShortcutOverrideState } from './custom-action-installer-shortcuts'
16
16
  import { getScriptPath } from './script-helper'
17
17
 
18
18
  type InstallerEntry = {
@@ -397,6 +397,7 @@ class InstallerSelectionDialog extends BasicDialog {
397
397
  debug(`[Setup] context menu row resolved item="${this.safeListItemText(contextItem, 1)}" entry="${entry?.action?.text ?? ''}"`)
398
398
  const canSetShortcut = Boolean(entry)
399
399
  const canResetShortcut = Boolean(entry && canResetSetupShortcut(entry))
400
+ const canResetAllShortcuts = this.entries.some(canResetSetupShortcut)
400
401
 
401
402
  const items = [
402
403
  {
@@ -413,6 +414,10 @@ class InstallerSelectionDialog extends BasicDialog {
413
414
  this.resetDefaultShortcut(entry)
414
415
  }
415
416
  },
417
+ {
418
+ text: 'Reset All Shortcuts to Defaults',
419
+ activated: () => this.resetAllDefaultShortcuts()
420
+ },
416
421
  {},
417
422
  { text: 'Check All', activated: () => this.checkVisible(listView, true) },
418
423
  { text: 'Uncheck All', activated: () => this.checkVisible(listView, false) }
@@ -421,6 +426,7 @@ class InstallerSelectionDialog extends BasicDialog {
421
426
  return new PopupMenuBuilder(this.builder.context).items(...items).build((menu) => {
422
427
  menu.setItemEnabled(0, canSetShortcut)
423
428
  menu.setItemEnabled(1, canResetShortcut)
429
+ menu.setItemEnabled(2, canResetAllShortcuts)
424
430
  })
425
431
  }
426
432
 
@@ -498,6 +504,15 @@ class InstallerSelectionDialog extends BasicDialog {
498
504
  this.refreshListEvent$.trigger()
499
505
  }
500
506
 
507
+ private resetAllDefaultShortcuts() {
508
+ const resettableEntries = this.entries.filter(canResetSetupShortcut)
509
+ if (resettableEntries.length === 0) return
510
+
511
+ resetSetupShortcuts(resettableEntries)
512
+ debug(`[Setup] reset ${resettableEntries.length} shortcuts to defaults`)
513
+ this.refreshListEvent$.trigger()
514
+ }
515
+
501
516
  private checkVisible(listView: DzListView, onOff: boolean) {
502
517
  if (!this.keywords$.value?.trim()) {
503
518
  checkAll(listView, onOff)
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest'
2
2
  import {
3
3
  canResetSetupShortcut,
4
4
  getDisplayedSetupShortcut,
5
+ resetSetupShortcuts,
5
6
  resetSetupShortcut,
6
7
  setSetupShortcut,
7
8
  updateShortcutOverrideState
@@ -80,6 +81,29 @@ describe('setup shortcut state', () => {
80
81
  expect(canResetSetupShortcut(entry)).toBe(false)
81
82
  })
82
83
 
84
+ it('resets all shortcut entries to their defaults', () => {
85
+ const installed = shortcutState({
86
+ installedActionName: 'installed-guid',
87
+ defaultShortcut: 'Ctrl+P',
88
+ installedShortcut: 'F12',
89
+ effectiveShortcut: 'F12',
90
+ isShortcutOverridden: true
91
+ })
92
+ const pending = shortcutState({
93
+ defaultShortcut: '',
94
+ effectiveShortcut: 'Ctrl+Alt+K'
95
+ })
96
+
97
+ resetSetupShortcuts([installed, pending])
98
+
99
+ expect(installed.installedShortcut).toBe('Ctrl+P')
100
+ expect(installed.effectiveShortcut).toBe('Ctrl+P')
101
+ expect(installed.isShortcutOverridden).toBe(false)
102
+ expect(pending.installedShortcut).toBe('')
103
+ expect(pending.effectiveShortcut).toBe('')
104
+ expect(pending.isShortcutOverridden).toBe(false)
105
+ })
106
+
83
107
  it('displays custom marker when installed shortcut was changed outside setup', () => {
84
108
  const entry = shortcutState({
85
109
  installedActionName: 'installed-guid',
@@ -47,3 +47,7 @@ export const resetSetupShortcut = (entry: InstallerShortcutState): void => {
47
47
  entry.effectiveShortcut = defaultShortcut
48
48
  updateShortcutOverrideState(entry)
49
49
  }
50
+
51
+ export const resetSetupShortcuts = (entries: InstallerShortcutState[]): void => {
52
+ entries.forEach(resetSetupShortcut)
53
+ }
@@ -1,207 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from 'vitest'
2
- import { BasicDialog } from './basic-dialog'
3
- import { setLogLevel } from '../common/log'
4
-
5
- class TestDialog extends BasicDialog {
6
- protected build(): void {
7
- this.builder.options({ resizable: true })
8
- }
9
- }
10
-
11
- class HeaderDialog extends BasicDialog {
12
- protected build(): void {
13
- this.builder.header({ text: 'Header' }).build()
14
- this.builder.options({ resizable: true })
15
- }
16
- }
17
-
18
- class NonResizableDefaultSizeDialog extends BasicDialog {
19
- protected build(): void { }
20
- }
21
-
22
- class ExplicitFixedHeightDialog extends BasicDialog {
23
- protected build(): void {
24
- this.builder.options({ height: 35 })
25
- }
26
- }
27
-
28
- describe('dialog geometry trace', () => {
29
- beforeEach(() => {
30
- ; (globalThis as any).App = {
31
- debug: vi.fn(),
32
- log: vi.fn(),
33
- warning: vi.fn(),
34
- flushLogBuffer: vi.fn(),
35
- statusLine: vi.fn()
36
- }
37
- ; (globalThis as any).DzBasicDialog = class {
38
- caption = ''
39
- width = 300
40
- height = 200
41
- minHeight = 0
42
- maxHeight = 0
43
- maxWidth = 0
44
- sizeGripEnabled = false
45
- fixedHeight: number | undefined
46
- fixedWidth: number | undefined
47
- private widget = {
48
- objectName: '',
49
- x: 10,
50
- y: 20,
51
- width: 300,
52
- height: 200,
53
- minimumSizeHint: { width: 300, height: 200 }
54
- }
55
-
56
- getWidget() {
57
- return this.widget
58
- }
59
-
60
- inherits(typeName: string) {
61
- return typeName === 'DzWidget'
62
- }
63
-
64
- addLayout() { }
65
-
66
- exec() {
67
- if (this.sizeGripEnabled) {
68
- this.width = 640
69
- this.height = 480
70
- this.widget.width = 640
71
- this.widget.height = 480
72
- return true
73
- }
74
-
75
- this.widget.minimumSizeHint = { width: 196, height: 76 }
76
- this.width = this.fixedWidth ?? this.widget.minimumSizeHint.width
77
- this.height = this.fixedHeight ?? this.widget.minimumSizeHint.height
78
- this.widget.width = this.width
79
- this.widget.height = this.height
80
- return true
81
- }
82
-
83
- setFixedHeight(value: number) {
84
- this.fixedHeight = value
85
- this.height = value
86
- }
87
-
88
- setFixedWidth(value: number) {
89
- this.fixedWidth = value
90
- this.width = value
91
- }
92
- }
93
- const Layout = class {
94
- direction = 0
95
- constructor(public parent: unknown) { }
96
- inherits(typeName: string) {
97
- return typeName === 'DzLayout'
98
- }
99
- addWidget() { }
100
- }
101
- class Widget {
102
- private _name = ''
103
- height = 30
104
- width = 100
105
- minHeight = 0
106
- maxHeight = 0
107
- toolTip = ''
108
- whatsThis = ''
109
- constructor(private parent: any) { }
110
- set name(value: string) {
111
- this._name = value
112
- if (this.parent?.getWidget) this.parent.getWidget().objectName = value
113
- }
114
- get name() {
115
- return this._name
116
- }
117
- getWidget() {
118
- return this
119
- }
120
- inherits(typeName: string) {
121
- return typeName === 'DzWidget'
122
- }
123
- setFixedHeight(value: number) {
124
- this.height = value
125
- }
126
- setFixedWidth(value: number) {
127
- this.width = value
128
- }
129
- show() { }
130
- hide() { }
131
- }
132
- ; (globalThis as any).DzVBoxLayout = Layout
133
- ; (globalThis as any).DzHBoxLayout = Layout
134
- ; (globalThis as any).DzWidget = Widget
135
- ; (globalThis as any).DzLabel = Widget
136
- ; (globalThis as any).DzTextBrowser = class extends Widget {
137
- html = ''
138
- readOnly = false
139
- lineWrapMode = 0
140
- wordWrapMode = 0
141
- }
142
- ; (globalThis as any).DzTextEdit = { WidgetWidth: 1, WordWrap: 2 }
143
- ; (globalThis as any).DzSplitter = { Vertical: 1, Horizontal: 2 }
144
- setLogLevel('trace')
145
- })
146
-
147
- it('logs dialog objectName and geometry before and after exec', () => {
148
- new TestDialog('Power Loader', 'Vholf3D Power Loader').run()
149
-
150
- const messages = (globalThis as any).App.debug.mock.calls.map((call: unknown[]) => String(call[0]))
151
-
152
- expect(messages.some((message: string) =>
153
- message.includes('event=dialog.geometry phase=init') &&
154
- message.includes('objectName="Vholf3DPowerLoaderDlg"')
155
- )).toBe(true)
156
- expect(messages.some((message: string) =>
157
- message.includes('event=dialog.geometry phase=beforeExec') &&
158
- message.includes('dialogWidth=300')
159
- )).toBe(true)
160
- expect(messages.some((message: string) =>
161
- message.includes('event=dialog.geometry phase=afterExec') &&
162
- message.includes('dialogWidth=640') &&
163
- message.includes('result=true')
164
- )).toBe(true)
165
- })
166
-
167
- it('keeps the DAZ geometry objectName stable after building a header widget', () => {
168
- new HeaderDialog('Power Menu - Active Tool', 'Vholf3D Power Menu - Active Tool').run()
169
-
170
- const messages = (globalThis as any).App.debug.mock.calls.map((call: unknown[]) => String(call[0]))
171
-
172
- expect(messages.some((message: string) =>
173
- message.includes('event=dialog.geometry phase=afterSetup') &&
174
- message.includes('objectName="Vholf3DPowerMenu-ActiveToolDlg"')
175
- )).toBe(true)
176
- expect(messages.some((message: string) =>
177
- message.includes('event=dialog.geometry phase=beforeExec') &&
178
- message.includes('objectName="Vholf3DPowerMenu-ActiveToolDlg"')
179
- )).toBe(true)
180
- })
181
-
182
- it('does not freeze a non-resizable dialog to an early implicit size hint', () => {
183
- new NonResizableDefaultSizeDialog('Store Active Camera').run()
184
-
185
- const messages = (globalThis as any).App.debug.mock.calls.map((call: unknown[]) => String(call[0]))
186
-
187
- expect(messages.some((message: string) =>
188
- message.includes('event=dialog.geometry phase=afterExec') &&
189
- message.includes('objectName="StoreActiveCameraDlg"') &&
190
- message.includes('dialogHeight=76') &&
191
- message.includes('sizeHintHeight=76')
192
- )).toBe(true)
193
- })
194
-
195
- it('keeps explicit non-resizable dialog height fixed', () => {
196
- new ExplicitFixedHeightDialog('Explicit Height').run()
197
-
198
- const messages = (globalThis as any).App.debug.mock.calls.map((call: unknown[]) => String(call[0]))
199
-
200
- expect(messages.some((message: string) =>
201
- message.includes('event=dialog.geometry phase=afterExec') &&
202
- message.includes('objectName="ExplicitHeightDlg"') &&
203
- message.includes('dialogHeight=35') &&
204
- message.includes('sizeHintHeight=76')
205
- )).toBe(true)
206
- })
207
- })