@gtkx/react 0.5.1 → 0.6.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.
- package/README.md +6 -28
- package/dist/codegen/jsx-generator.d.ts +15 -9
- package/dist/codegen/jsx-generator.js +215 -94
- package/dist/factory.js +5 -0
- package/dist/generated/internal.js +9291 -3379
- package/dist/generated/jsx.d.ts +12954 -4396
- package/dist/generated/jsx.js +3672 -171
- package/dist/node.js +25 -7
- package/dist/nodes/header-bar.d.ts +19 -0
- package/dist/nodes/header-bar.js +45 -0
- package/dist/nodes/toolbar-view.d.ts +19 -0
- package/dist/nodes/toolbar-view.js +81 -0
- package/dist/nodes/window.js +11 -2
- package/dist/portal.d.ts +1 -1
- package/dist/portal.js +1 -1
- package/dist/predicates.d.ts +7 -0
- package/dist/predicates.js +4 -0
- package/dist/reconciler.js +1 -1
- package/package.json +3 -3
|
@@ -7,14 +7,16 @@ const GRID_WIDGETS = new Set(["Grid"]);
|
|
|
7
7
|
const NOTEBOOK_WIDGET = "Notebook";
|
|
8
8
|
const STACK_WIDGET = "Stack";
|
|
9
9
|
const POPOVER_MENU_WIDGET = "PopoverMenu";
|
|
10
|
+
const TOOLBAR_VIEW_WIDGET = "ToolbarView";
|
|
10
11
|
const INTERNALLY_PROVIDED_PARAMS = {
|
|
11
12
|
ApplicationWindow: new Set(["application"]),
|
|
12
13
|
};
|
|
14
|
+
const WIDGET_REFERENCE_PROPERTIES = new Set(["mnemonic-widget"]);
|
|
13
15
|
const isPrimitive = (tsType) => {
|
|
14
16
|
const primitives = new Set(["boolean", "number", "string", "void", "unknown", "null", "undefined"]);
|
|
15
17
|
return primitives.has(tsType);
|
|
16
18
|
};
|
|
17
|
-
const
|
|
19
|
+
const toJsxPropertyTypeBase = (tsType, namespace) => {
|
|
18
20
|
let result = tsType;
|
|
19
21
|
if (result.startsWith("Ref<")) {
|
|
20
22
|
result = result.replace(/^Ref<(.+)>$/, "$1");
|
|
@@ -28,7 +30,7 @@ const toJsxPropertyType = (tsType) => {
|
|
|
28
30
|
}
|
|
29
31
|
if (result.includes(".") || result.includes("<") || result.includes("("))
|
|
30
32
|
return result;
|
|
31
|
-
return
|
|
33
|
+
return `${namespace}.${result}`;
|
|
32
34
|
};
|
|
33
35
|
const isListWidget = (widgetName) => LIST_WIDGETS.has(widgetName);
|
|
34
36
|
const isColumnViewWidget = (widgetName) => widgetName === COLUMN_VIEW_WIDGET;
|
|
@@ -37,9 +39,10 @@ const isGridWidget = (widgetName) => GRID_WIDGETS.has(widgetName);
|
|
|
37
39
|
const isNotebookWidget = (widgetName) => widgetName === NOTEBOOK_WIDGET;
|
|
38
40
|
const isStackWidget = (widgetName) => widgetName === STACK_WIDGET;
|
|
39
41
|
const isPopoverMenuWidget = (widgetName) => widgetName === POPOVER_MENU_WIDGET;
|
|
42
|
+
const isToolbarViewWidget = (widgetName) => widgetName === TOOLBAR_VIEW_WIDGET;
|
|
40
43
|
const sanitizeDoc = (doc) => {
|
|
41
44
|
let result = doc;
|
|
42
|
-
result = result.replace(/<picture
|
|
45
|
+
result = result.replace(/<picture[^>]*>[\s\S]*?<\/picture>/gi, "");
|
|
43
46
|
result = result.replace(/<img[^>]*>/gi, "");
|
|
44
47
|
result = result.replace(/<source[^>]*>/gi, "");
|
|
45
48
|
result = result.replace(/!\[[^\]]*\]\([^)]+\.png\)/gi, "");
|
|
@@ -48,6 +51,7 @@ const sanitizeDoc = (doc) => {
|
|
|
48
51
|
result = result.replace(/<\/kbd>/gi, "`");
|
|
49
52
|
result = result.replace(/\[([^\]]+)\]\([^)]+\.html[^)]*\)/gi, "$1");
|
|
50
53
|
result = result.replace(/@(\w+)\s/g, "`$1` ");
|
|
54
|
+
result = result.replace(/<(\/?)(child|object|property|signal|template)>/gi, "`<$1$2>`");
|
|
51
55
|
return result.trim();
|
|
52
56
|
};
|
|
53
57
|
const formatDoc = (doc, indent = "") => {
|
|
@@ -82,52 +86,66 @@ const isWidgetSubclass = (typeName, classMap, visited = new Set()) => {
|
|
|
82
86
|
*/
|
|
83
87
|
export class JsxGenerator {
|
|
84
88
|
typeMapper;
|
|
89
|
+
typeRegistry;
|
|
90
|
+
classMap;
|
|
85
91
|
options;
|
|
86
|
-
classMap = new Map();
|
|
87
92
|
interfaceMap = new Map();
|
|
88
|
-
|
|
89
|
-
usedExternalNamespaces = new Set();
|
|
93
|
+
usedNamespaces = new Set();
|
|
90
94
|
widgetPropertyNames = new Set();
|
|
91
95
|
widgetSignalNames = new Set();
|
|
96
|
+
currentNamespace = "";
|
|
97
|
+
widgetNamespaceMap = new Map();
|
|
92
98
|
/**
|
|
93
99
|
* Creates a new JSX generator.
|
|
94
100
|
* @param typeMapper - TypeMapper for converting GIR types to TypeScript
|
|
101
|
+
* @param typeRegistry - TypeRegistry for cross-namespace type resolution
|
|
102
|
+
* @param classMap - Combined class map with fully qualified names
|
|
95
103
|
* @param options - Generator configuration options
|
|
96
104
|
*/
|
|
97
|
-
constructor(typeMapper, options = {}) {
|
|
105
|
+
constructor(typeMapper, typeRegistry, classMap, options = {}) {
|
|
98
106
|
this.typeMapper = typeMapper;
|
|
107
|
+
this.typeRegistry = typeRegistry;
|
|
108
|
+
this.classMap = classMap;
|
|
99
109
|
this.options = options;
|
|
100
110
|
}
|
|
101
111
|
/**
|
|
102
|
-
* Generates JSX type definitions for all widgets in
|
|
103
|
-
* @param
|
|
104
|
-
* @param classMap - Map of class names to class definitions
|
|
112
|
+
* Generates JSX type definitions for all widgets in the given namespaces.
|
|
113
|
+
* @param namespaces - The parsed GIR namespaces (GTK must be first)
|
|
105
114
|
* @returns Generated TypeScript code as public jsx.ts and internal.ts files
|
|
106
115
|
*/
|
|
107
|
-
async generate(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
116
|
+
async generate(namespaces) {
|
|
117
|
+
for (const ns of namespaces) {
|
|
118
|
+
for (const iface of ns.interfaces) {
|
|
119
|
+
this.interfaceMap.set(iface.name, iface);
|
|
120
|
+
this.interfaceMap.set(`${ns.name}.${iface.name}`, iface);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
this.usedNamespaces.clear();
|
|
124
|
+
this.widgetNamespaceMap.clear();
|
|
125
|
+
const allWidgets = [];
|
|
126
|
+
for (const ns of namespaces) {
|
|
127
|
+
const widgets = this.findWidgets(ns);
|
|
128
|
+
for (const widget of widgets) {
|
|
129
|
+
allWidgets.push({ widget, namespace: ns.name });
|
|
130
|
+
this.widgetNamespaceMap.set(widget.name, ns.name);
|
|
131
|
+
this.widgetNamespaceMap.set(`${ns.name}.${widget.name}`, ns.name);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const containerMetadata = this.buildContainerMetadata(allWidgets);
|
|
135
|
+
const widgetClass = this.classMap.get("Widget") ?? this.classMap.get("Gtk.Widget");
|
|
115
136
|
this.widgetPropertyNames = new Set(widgetClass?.properties.map((p) => toCamelCase(p.name)) ?? []);
|
|
116
137
|
this.widgetSignalNames = new Set(widgetClass?.signals.map((s) => toCamelCase(s.name)) ?? []);
|
|
117
|
-
const
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
this.generateJsxNamespace(widgets, containerMetadata),
|
|
124
|
-
"export {};",
|
|
125
|
-
];
|
|
138
|
+
const commonTypes = this.generateCommonTypes(widgetClass);
|
|
139
|
+
const widgetPropsInterfaces = this.generateWidgetPropsInterfaces(allWidgets, containerMetadata);
|
|
140
|
+
const exports = this.generateExports(allWidgets, containerMetadata);
|
|
141
|
+
const jsxNamespace = this.generateJsxNamespace(allWidgets, containerMetadata);
|
|
142
|
+
const imports = this.generateImports();
|
|
143
|
+
const jsxSections = [imports, commonTypes, widgetPropsInterfaces, exports, jsxNamespace, "export {};"];
|
|
126
144
|
const internalSections = [
|
|
127
145
|
this.generateInternalImports(),
|
|
128
|
-
this.generateConstructorArgsMetadata(
|
|
129
|
-
this.generatePropSettersMap(
|
|
130
|
-
this.generateSetterGetterMap(
|
|
146
|
+
this.generateConstructorArgsMetadata(allWidgets),
|
|
147
|
+
this.generatePropSettersMap(allWidgets),
|
|
148
|
+
this.generateSetterGetterMap(allWidgets),
|
|
131
149
|
];
|
|
132
150
|
return {
|
|
133
151
|
jsx: await this.formatCode(jsxSections.join("\n")),
|
|
@@ -135,15 +153,15 @@ export class JsxGenerator {
|
|
|
135
153
|
};
|
|
136
154
|
}
|
|
137
155
|
generateImports() {
|
|
138
|
-
|
|
156
|
+
this.usedNamespaces.add("Gtk");
|
|
157
|
+
const namespaceImports = [...this.usedNamespaces]
|
|
139
158
|
.sort()
|
|
140
159
|
.map((ns) => `import type * as ${ns} from "@gtkx/ffi/${ns.toLowerCase()}";`);
|
|
141
160
|
return [
|
|
142
161
|
`import "react";`,
|
|
143
162
|
`import { createElement } from "react";`,
|
|
144
163
|
`import type { ReactNode, Ref } from "react";`,
|
|
145
|
-
...
|
|
146
|
-
`import type * as Gtk from "@gtkx/ffi/gtk";`,
|
|
164
|
+
...namespaceImports,
|
|
147
165
|
`import type { ColumnViewColumnProps, ColumnViewRootProps, GridChildProps, ListItemProps, ListViewRenderProps, MenuItemProps, MenuRootProps, MenuSectionProps, MenuSubmenuProps, NotebookPageProps, SlotProps, StackPageProps, StackRootProps } from "../types.js";`,
|
|
148
166
|
"",
|
|
149
167
|
].join("\n");
|
|
@@ -152,6 +170,8 @@ export class JsxGenerator {
|
|
|
152
170
|
return "/** Internal metadata for the reconciler. Not part of the public API. */\n";
|
|
153
171
|
}
|
|
154
172
|
generateCommonTypes(widgetClass) {
|
|
173
|
+
this.currentNamespace = "Gtk";
|
|
174
|
+
this.typeMapper.setTypeRegistry(this.typeRegistry, "Gtk");
|
|
155
175
|
const widgetPropsContent = this.generateWidgetPropsContent(widgetClass);
|
|
156
176
|
return `
|
|
157
177
|
export { ColumnViewColumnProps, ColumnViewRootProps, GridChildProps, ListItemProps, ListViewRenderProps, MenuItemProps, MenuRootProps, MenuSectionProps, MenuSubmenuProps, NotebookPageProps, SlotProps, StackPageProps, StackRootProps };
|
|
@@ -169,7 +189,7 @@ ${widgetPropsContent}
|
|
|
169
189
|
if (widgetClass) {
|
|
170
190
|
for (const prop of widgetClass.properties) {
|
|
171
191
|
const propName = toCamelCase(prop.name);
|
|
172
|
-
const tsType = toJsxPropertyType(this.typeMapper.mapType(prop.type).ts);
|
|
192
|
+
const tsType = this.toJsxPropertyType(this.typeMapper.mapType(prop.type).ts, "Gtk");
|
|
173
193
|
if (prop.doc) {
|
|
174
194
|
lines.push(formatDoc(prop.doc, "\t").trimEnd());
|
|
175
195
|
}
|
|
@@ -190,33 +210,39 @@ ${widgetPropsContent}
|
|
|
190
210
|
lines.push("}");
|
|
191
211
|
return lines.join("\n");
|
|
192
212
|
}
|
|
193
|
-
buildContainerMetadata(widgets
|
|
213
|
+
buildContainerMetadata(widgets) {
|
|
194
214
|
const metadata = new Map();
|
|
195
|
-
for (const widget of widgets) {
|
|
196
|
-
|
|
215
|
+
for (const { widget, namespace } of widgets) {
|
|
216
|
+
const key = `${namespace}.${widget.name}`;
|
|
217
|
+
metadata.set(key, this.analyzeContainerCapabilities(widget));
|
|
197
218
|
}
|
|
198
219
|
return metadata;
|
|
199
220
|
}
|
|
200
|
-
findWidgets(namespace
|
|
221
|
+
findWidgets(namespace) {
|
|
201
222
|
const widgetCache = new Map();
|
|
202
|
-
const checkIsWidget = (className) => {
|
|
203
|
-
const
|
|
223
|
+
const checkIsWidget = (className, ns) => {
|
|
224
|
+
const cacheKey = `${ns}.${className}`;
|
|
225
|
+
const cached = widgetCache.get(cacheKey);
|
|
204
226
|
if (cached !== undefined)
|
|
205
227
|
return cached;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
228
|
+
widgetCache.set(cacheKey, false);
|
|
229
|
+
const cls = this.classMap.get(cacheKey) ?? this.classMap.get(className);
|
|
230
|
+
if (!cls)
|
|
209
231
|
return false;
|
|
210
|
-
}
|
|
211
232
|
if (cls.name === "Widget") {
|
|
212
|
-
widgetCache.set(
|
|
233
|
+
widgetCache.set(cacheKey, true);
|
|
213
234
|
return true;
|
|
214
235
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
236
|
+
if (cls.parent) {
|
|
237
|
+
const parentNs = cls.parent.includes(".") ? cls.parent.split(".")[0] : ns;
|
|
238
|
+
const parentName = cls.parent.includes(".") ? cls.parent.split(".")[1] : cls.parent;
|
|
239
|
+
const result = checkIsWidget(parentName ?? "", parentNs ?? ns);
|
|
240
|
+
widgetCache.set(cacheKey, result);
|
|
241
|
+
return result;
|
|
242
|
+
}
|
|
243
|
+
return false;
|
|
218
244
|
};
|
|
219
|
-
const widgets = namespace.classes.filter((cls) => checkIsWidget(cls.name));
|
|
245
|
+
const widgets = namespace.classes.filter((cls) => checkIsWidget(cls.name, namespace.name));
|
|
220
246
|
return widgets.sort((a, b) => {
|
|
221
247
|
if (a.name === "Widget")
|
|
222
248
|
return -1;
|
|
@@ -229,20 +255,26 @@ ${widgetPropsContent}
|
|
|
229
255
|
return a.name.localeCompare(b.name);
|
|
230
256
|
});
|
|
231
257
|
}
|
|
232
|
-
analyzeContainerCapabilities(widget
|
|
258
|
+
analyzeContainerCapabilities(widget) {
|
|
233
259
|
const hasAppend = widget.methods.some((m) => m.name === "append");
|
|
234
260
|
const hasSetChild = widget.methods.some((m) => m.name === "set_child");
|
|
235
261
|
const namedChildSlots = widget.properties
|
|
236
262
|
.filter((prop) => {
|
|
237
263
|
if (!prop.writable)
|
|
238
264
|
return false;
|
|
265
|
+
if (WIDGET_REFERENCE_PROPERTIES.has(prop.name))
|
|
266
|
+
return false;
|
|
239
267
|
const typeName = prop.type.name;
|
|
240
|
-
return typeName === "Gtk.Widget" || typeName === "Widget" || isWidgetSubclass(typeName, classMap);
|
|
268
|
+
return typeName === "Gtk.Widget" || typeName === "Widget" || isWidgetSubclass(typeName, this.classMap);
|
|
241
269
|
})
|
|
242
270
|
.map((prop) => ({
|
|
243
271
|
propertyName: prop.name,
|
|
244
272
|
slotName: toPascalCase(prop.name),
|
|
245
273
|
}));
|
|
274
|
+
if (isToolbarViewWidget(widget.name)) {
|
|
275
|
+
namedChildSlots.push({ propertyName: "top", slotName: "Top" });
|
|
276
|
+
namedChildSlots.push({ propertyName: "bottom", slotName: "Bottom" });
|
|
277
|
+
}
|
|
246
278
|
return {
|
|
247
279
|
supportsMultipleChildren: hasAppend,
|
|
248
280
|
supportsSingleChild: hasSetChild,
|
|
@@ -251,18 +283,28 @@ ${widgetPropsContent}
|
|
|
251
283
|
}
|
|
252
284
|
generateWidgetPropsInterfaces(widgets, containerMetadata) {
|
|
253
285
|
const sections = [];
|
|
254
|
-
for (const widget of widgets) {
|
|
286
|
+
for (const { widget, namespace } of widgets) {
|
|
255
287
|
if (widget.name === "Widget")
|
|
256
288
|
continue;
|
|
257
|
-
const
|
|
289
|
+
const metadataKey = `${namespace}.${widget.name}`;
|
|
290
|
+
const metadata = containerMetadata.get(metadataKey);
|
|
258
291
|
if (!metadata)
|
|
259
|
-
throw new Error(`Missing container metadata for widget: ${
|
|
292
|
+
throw new Error(`Missing container metadata for widget: ${metadataKey}`);
|
|
293
|
+
this.currentNamespace = namespace;
|
|
294
|
+
this.usedNamespaces.add(namespace);
|
|
295
|
+
this.typeMapper.setTypeRegistry(this.typeRegistry, namespace);
|
|
260
296
|
sections.push(this.generateWidgetProps(widget, metadata));
|
|
261
297
|
}
|
|
262
298
|
return sections.join("\n");
|
|
263
299
|
}
|
|
300
|
+
getWidgetExportName(widget) {
|
|
301
|
+
const baseName = toPascalCase(widget.name);
|
|
302
|
+
if (this.currentNamespace === "Gtk")
|
|
303
|
+
return baseName;
|
|
304
|
+
return `${this.currentNamespace}${baseName}`;
|
|
305
|
+
}
|
|
264
306
|
generateWidgetProps(widget, metadata) {
|
|
265
|
-
const widgetName =
|
|
307
|
+
const widgetName = this.getWidgetExportName(widget);
|
|
266
308
|
const parentPropsName = this.getParentPropsName(widget);
|
|
267
309
|
const namedChildPropNames = new Set(metadata.namedChildSlots.map((s) => toCamelCase(s.propertyName)));
|
|
268
310
|
const lines = [];
|
|
@@ -308,10 +350,9 @@ ${widgetPropsContent}
|
|
|
308
350
|
const propName = toCamelCase(prop.name);
|
|
309
351
|
emittedProps.add(prop.name);
|
|
310
352
|
const typeMapping = this.typeMapper.mapType(prop.type);
|
|
311
|
-
const tsType = toJsxPropertyType(typeMapping.ts);
|
|
312
|
-
const isRequiredByProperty = prop.constructOnly && !prop.hasDefault;
|
|
353
|
+
const tsType = this.toJsxPropertyType(typeMapping.ts, this.currentNamespace);
|
|
313
354
|
const isRequiredByConstructor = requiredCtorParams.has(prop.name);
|
|
314
|
-
const isRequired =
|
|
355
|
+
const isRequired = isRequiredByConstructor;
|
|
315
356
|
if (prop.doc) {
|
|
316
357
|
lines.push(formatDoc(prop.doc, "\t").trimEnd());
|
|
317
358
|
}
|
|
@@ -324,7 +365,7 @@ ${widgetPropsContent}
|
|
|
324
365
|
const inheritedProp = this.findInheritedProperty(widget, paramName);
|
|
325
366
|
if (inheritedProp) {
|
|
326
367
|
const typeMapping = this.typeMapper.mapType(inheritedProp.type);
|
|
327
|
-
const tsType = toJsxPropertyType(typeMapping.ts);
|
|
368
|
+
const tsType = this.toJsxPropertyType(typeMapping.ts, this.currentNamespace);
|
|
328
369
|
lines.push(`\t${propName}: ${tsType};`);
|
|
329
370
|
}
|
|
330
371
|
}
|
|
@@ -338,7 +379,7 @@ ${widgetPropsContent}
|
|
|
338
379
|
if (signal.doc) {
|
|
339
380
|
lines.push(formatDoc(signal.doc, "\t").trimEnd());
|
|
340
381
|
}
|
|
341
|
-
lines.push(`\t${this.generateSignalHandler(signal,
|
|
382
|
+
lines.push(`\t${this.generateSignalHandler(signal, widget.name)}`);
|
|
342
383
|
}
|
|
343
384
|
}
|
|
344
385
|
if (isListWidget(widget.name)) {
|
|
@@ -357,7 +398,8 @@ ${widgetPropsContent}
|
|
|
357
398
|
lines.push(`\tonSelectionChanged?: (item: unknown, index: number) => void;`);
|
|
358
399
|
}
|
|
359
400
|
lines.push("");
|
|
360
|
-
|
|
401
|
+
const ffiTypeName = toPascalCase(widget.name);
|
|
402
|
+
lines.push(`\tref?: Ref<${this.currentNamespace}.${ffiTypeName}>;`);
|
|
361
403
|
lines.push(`}`);
|
|
362
404
|
return `${lines.join("\n")}\n`;
|
|
363
405
|
}
|
|
@@ -366,11 +408,19 @@ ${widgetPropsContent}
|
|
|
366
408
|
return "WidgetProps";
|
|
367
409
|
if (widget.name === "ApplicationWindow")
|
|
368
410
|
return "WindowProps";
|
|
369
|
-
if (widget.parent
|
|
411
|
+
if (!widget.parent)
|
|
370
412
|
return "WidgetProps";
|
|
371
|
-
|
|
413
|
+
const parentNs = widget.parent.includes(".") ? widget.parent.split(".")[0] : this.currentNamespace;
|
|
414
|
+
const parentName = widget.parent.includes(".") ? widget.parent.split(".")[1] : widget.parent;
|
|
415
|
+
if (parentName === "Widget")
|
|
416
|
+
return "WidgetProps";
|
|
417
|
+
if (parentName === "Window")
|
|
372
418
|
return "WindowProps";
|
|
373
|
-
|
|
419
|
+
const baseName = toPascalCase(parentName ?? "");
|
|
420
|
+
if (parentNs === "Gtk") {
|
|
421
|
+
return `${baseName}Props`;
|
|
422
|
+
}
|
|
423
|
+
return `${parentNs}${baseName}Props`;
|
|
374
424
|
}
|
|
375
425
|
getRequiredConstructorParams(widget) {
|
|
376
426
|
const required = new Set();
|
|
@@ -392,18 +442,22 @@ ${widgetPropsContent}
|
|
|
392
442
|
const mainCtor = widget.constructors.find((c) => c.name === "new");
|
|
393
443
|
if (!mainCtor)
|
|
394
444
|
return [];
|
|
395
|
-
|
|
445
|
+
const params = mainCtor.parameters.map((param) => ({
|
|
396
446
|
name: toCamelCase(param.name),
|
|
397
447
|
hasDefault: param.nullable || param.optional || false,
|
|
398
448
|
}));
|
|
449
|
+
const required = params.filter((p) => !p.hasDefault);
|
|
450
|
+
const optional = params.filter((p) => p.hasDefault);
|
|
451
|
+
return [...required, ...optional];
|
|
399
452
|
}
|
|
400
453
|
generateConstructorArgsMetadata(widgets) {
|
|
401
454
|
const entries = [];
|
|
402
|
-
for (const widget of widgets) {
|
|
455
|
+
for (const { widget, namespace } of widgets) {
|
|
403
456
|
const params = this.getConstructorParams(widget);
|
|
404
457
|
if (params.length === 0)
|
|
405
458
|
continue;
|
|
406
|
-
|
|
459
|
+
this.currentNamespace = namespace;
|
|
460
|
+
const widgetName = this.getWidgetExportName(widget);
|
|
407
461
|
const paramStrs = params.map((p) => `{ name: "${p.name}", hasDefault: ${p.hasDefault} }`);
|
|
408
462
|
entries.push(`\t${widgetName}: [${paramStrs.join(", ")}]`);
|
|
409
463
|
}
|
|
@@ -414,7 +468,8 @@ ${widgetPropsContent}
|
|
|
414
468
|
}
|
|
415
469
|
generatePropSettersMap(widgets) {
|
|
416
470
|
const widgetEntries = [];
|
|
417
|
-
for (const widget of widgets) {
|
|
471
|
+
for (const { widget, namespace } of widgets) {
|
|
472
|
+
this.currentNamespace = namespace;
|
|
418
473
|
const propSetterPairs = [];
|
|
419
474
|
const allProps = this.collectAllProperties(widget);
|
|
420
475
|
for (const prop of allProps) {
|
|
@@ -425,7 +480,7 @@ ${widgetPropsContent}
|
|
|
425
480
|
}
|
|
426
481
|
}
|
|
427
482
|
if (propSetterPairs.length > 0) {
|
|
428
|
-
const widgetName =
|
|
483
|
+
const widgetName = this.getWidgetExportName(widget);
|
|
429
484
|
widgetEntries.push(`\t${widgetName}: { ${propSetterPairs.join(", ")} }`);
|
|
430
485
|
}
|
|
431
486
|
}
|
|
@@ -436,18 +491,24 @@ ${widgetPropsContent}
|
|
|
436
491
|
}
|
|
437
492
|
generateSetterGetterMap(widgets) {
|
|
438
493
|
const widgetEntries = [];
|
|
439
|
-
for (const widget of widgets) {
|
|
494
|
+
for (const { widget, namespace } of widgets) {
|
|
495
|
+
this.currentNamespace = namespace;
|
|
440
496
|
const setterGetterPairs = [];
|
|
441
497
|
const allProps = this.collectAllProperties(widget);
|
|
498
|
+
const parentMethodNames = this.collectParentMethodNames(widget);
|
|
499
|
+
const widgetClassName = toPascalCase(widget.name);
|
|
442
500
|
for (const prop of allProps) {
|
|
443
501
|
if (prop.setter && prop.getter) {
|
|
444
502
|
const setterName = toCamelCase(prop.setter);
|
|
445
|
-
|
|
503
|
+
let getterName = toCamelCase(prop.getter);
|
|
504
|
+
if (parentMethodNames.has(prop.getter)) {
|
|
505
|
+
getterName = `${getterName}${widgetClassName}`;
|
|
506
|
+
}
|
|
446
507
|
setterGetterPairs.push(`"${setterName}": "${getterName}"`);
|
|
447
508
|
}
|
|
448
509
|
}
|
|
449
510
|
if (setterGetterPairs.length > 0) {
|
|
450
|
-
const widgetName =
|
|
511
|
+
const widgetName = this.getWidgetExportName(widget);
|
|
451
512
|
widgetEntries.push(`\t${widgetName}: { ${setterGetterPairs.join(", ")} }`);
|
|
452
513
|
}
|
|
453
514
|
}
|
|
@@ -456,6 +517,32 @@ ${widgetPropsContent}
|
|
|
456
517
|
}
|
|
457
518
|
return `export const SETTER_GETTERS: Record<string, Record<string, string>> = {\n${widgetEntries.join(",\n")},\n};\n`;
|
|
458
519
|
}
|
|
520
|
+
collectParentMethodNames(widget) {
|
|
521
|
+
const names = new Set();
|
|
522
|
+
const visited = new Set();
|
|
523
|
+
let parentRef = widget.parent;
|
|
524
|
+
while (parentRef && !visited.has(parentRef)) {
|
|
525
|
+
visited.add(parentRef);
|
|
526
|
+
const current = this.classMap.get(parentRef) ??
|
|
527
|
+
this.classMap.get(`${this.currentNamespace}.${parentRef}`) ??
|
|
528
|
+
this.classMap.get(`Gtk.${parentRef}`);
|
|
529
|
+
if (!current)
|
|
530
|
+
break;
|
|
531
|
+
for (const method of current.methods) {
|
|
532
|
+
names.add(method.name);
|
|
533
|
+
}
|
|
534
|
+
for (const ifaceName of current.implements) {
|
|
535
|
+
const iface = this.interfaceMap.get(ifaceName);
|
|
536
|
+
if (iface) {
|
|
537
|
+
for (const method of iface.methods) {
|
|
538
|
+
names.add(method.name);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
parentRef = current.parent;
|
|
543
|
+
}
|
|
544
|
+
return names;
|
|
545
|
+
}
|
|
459
546
|
collectAllProperties(widget) {
|
|
460
547
|
const props = [];
|
|
461
548
|
const seen = new Set();
|
|
@@ -478,7 +565,15 @@ ${widgetPropsContent}
|
|
|
478
565
|
}
|
|
479
566
|
}
|
|
480
567
|
}
|
|
481
|
-
|
|
568
|
+
if (current.parent) {
|
|
569
|
+
current =
|
|
570
|
+
this.classMap.get(current.parent) ??
|
|
571
|
+
this.classMap.get(`${this.currentNamespace}.${current.parent}`) ??
|
|
572
|
+
this.classMap.get(`Gtk.${current.parent}`);
|
|
573
|
+
}
|
|
574
|
+
else {
|
|
575
|
+
current = undefined;
|
|
576
|
+
}
|
|
482
577
|
}
|
|
483
578
|
return props;
|
|
484
579
|
}
|
|
@@ -530,18 +625,22 @@ ${widgetPropsContent}
|
|
|
530
625
|
return undefined;
|
|
531
626
|
if (typeName.includes(".")) {
|
|
532
627
|
const [ns, className] = typeName.split(".", 2);
|
|
533
|
-
if (ns
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
628
|
+
if (ns && className) {
|
|
629
|
+
const registered = this.typeRegistry.resolve(`${ns}.${className}`);
|
|
630
|
+
if (registered && (registered.kind === "class" || registered.kind === "interface")) {
|
|
631
|
+
this.usedNamespaces.add(ns);
|
|
632
|
+
return `${ns}.${registered.transformedName}`;
|
|
633
|
+
}
|
|
539
634
|
}
|
|
540
635
|
return undefined;
|
|
541
636
|
}
|
|
542
|
-
const
|
|
543
|
-
if (
|
|
544
|
-
|
|
637
|
+
const registered = this.typeRegistry.resolveInNamespace(typeName, this.currentNamespace);
|
|
638
|
+
if (registered && (registered.kind === "class" || registered.kind === "interface")) {
|
|
639
|
+
this.usedNamespaces.add(registered.namespace);
|
|
640
|
+
if (registered.namespace === this.currentNamespace) {
|
|
641
|
+
return `${this.currentNamespace}.${registered.transformedName}`;
|
|
642
|
+
}
|
|
643
|
+
return `${registered.namespace}.${registered.transformedName}`;
|
|
545
644
|
}
|
|
546
645
|
return undefined;
|
|
547
646
|
}
|
|
@@ -549,12 +648,30 @@ ${widgetPropsContent}
|
|
|
549
648
|
const primitives = new Set(["boolean", "number", "string", "void", "unknown", "null", "undefined"]);
|
|
550
649
|
if (primitives.has(tsType))
|
|
551
650
|
return tsType;
|
|
651
|
+
if (tsType.endsWith("[]")) {
|
|
652
|
+
const elementType = tsType.slice(0, -2);
|
|
653
|
+
if (primitives.has(elementType))
|
|
654
|
+
return tsType;
|
|
655
|
+
if (elementType.includes(".") || elementType.includes("<") || elementType.includes("("))
|
|
656
|
+
return tsType;
|
|
657
|
+
return `${this.currentNamespace}.${elementType}[]`;
|
|
658
|
+
}
|
|
552
659
|
if (tsType.includes(".") || tsType.includes("<") || tsType.includes("("))
|
|
553
660
|
return tsType;
|
|
554
|
-
return
|
|
661
|
+
return `${this.currentNamespace}.${tsType}`;
|
|
662
|
+
}
|
|
663
|
+
toJsxPropertyType(tsType, namespace) {
|
|
664
|
+
const result = toJsxPropertyTypeBase(tsType, namespace);
|
|
665
|
+
if (result.includes(".") && !result.includes("<") && !result.includes("(")) {
|
|
666
|
+
const ns = result.split(".")[0];
|
|
667
|
+
if (ns) {
|
|
668
|
+
this.usedNamespaces.add(ns);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
return result;
|
|
555
672
|
}
|
|
556
673
|
buildSignalHandlerType(signal, widgetName) {
|
|
557
|
-
const selfParam = `self:
|
|
674
|
+
const selfParam = `self: ${this.currentNamespace}.${toPascalCase(widgetName)}`;
|
|
558
675
|
const otherParams = signal.parameters
|
|
559
676
|
?.map((p) => {
|
|
560
677
|
const ffiType = this.getSignalParamFfiType(p.type.name);
|
|
@@ -574,11 +691,13 @@ ${widgetPropsContent}
|
|
|
574
691
|
}
|
|
575
692
|
generateExports(widgets, containerMetadata) {
|
|
576
693
|
const lines = [];
|
|
577
|
-
for (const widget of widgets) {
|
|
578
|
-
|
|
579
|
-
const
|
|
694
|
+
for (const { widget, namespace } of widgets) {
|
|
695
|
+
this.currentNamespace = namespace;
|
|
696
|
+
const widgetName = this.getWidgetExportName(widget);
|
|
697
|
+
const metadataKey = `${namespace}.${widget.name}`;
|
|
698
|
+
const metadata = containerMetadata.get(metadataKey);
|
|
580
699
|
if (!metadata)
|
|
581
|
-
throw new Error(`Missing container metadata for widget: ${
|
|
700
|
+
throw new Error(`Missing container metadata for widget: ${metadataKey}`);
|
|
582
701
|
const nonChildSlots = metadata.namedChildSlots.filter((slot) => slot.slotName !== "Child");
|
|
583
702
|
const hasMeaningfulSlots = nonChildSlots.length > 0 ||
|
|
584
703
|
isListWidget(widget.name) ||
|
|
@@ -595,8 +714,8 @@ ${widgetPropsContent}
|
|
|
595
714
|
isDropDownWidget(widget.name) ||
|
|
596
715
|
isStackWidget(widget.name) ||
|
|
597
716
|
isPopoverMenuWidget(widget.name)) {
|
|
598
|
-
const wrapperComponents = this.generateGenericWrapperComponents(
|
|
599
|
-
const exportMembers = this.getWrapperExportMembers(
|
|
717
|
+
const wrapperComponents = this.generateGenericWrapperComponents(widgetName, metadata);
|
|
718
|
+
const exportMembers = this.getWrapperExportMembers(widgetName, metadata);
|
|
600
719
|
if (docComment) {
|
|
601
720
|
lines.push(`${wrapperComponents}\n${docComment}\nexport const ${widgetName} = {\n\t${exportMembers.join(",\n\t")},\n};`);
|
|
602
721
|
}
|
|
@@ -794,14 +913,16 @@ export const Menu = {
|
|
|
794
913
|
}
|
|
795
914
|
generateJsxNamespace(widgets, containerMetadata) {
|
|
796
915
|
const elements = [];
|
|
797
|
-
for (const widget of widgets) {
|
|
916
|
+
for (const { widget, namespace } of widgets) {
|
|
798
917
|
if (widget.name === "Widget")
|
|
799
918
|
continue;
|
|
800
|
-
|
|
919
|
+
this.currentNamespace = namespace;
|
|
920
|
+
const widgetName = this.getWidgetExportName(widget);
|
|
801
921
|
const propsName = `${widgetName}Props`;
|
|
802
|
-
const
|
|
922
|
+
const metadataKey = `${namespace}.${widget.name}`;
|
|
923
|
+
const metadata = containerMetadata.get(metadataKey);
|
|
803
924
|
if (!metadata)
|
|
804
|
-
throw new Error(`Missing container metadata for widget: ${
|
|
925
|
+
throw new Error(`Missing container metadata for widget: ${metadataKey}`);
|
|
805
926
|
const nonChildSlots = metadata.namedChildSlots.filter((slot) => slot.slotName !== "Child");
|
|
806
927
|
const hasMeaningfulSlots = nonChildSlots.length > 0 ||
|
|
807
928
|
isListWidget(widget.name) ||
|
package/dist/factory.js
CHANGED
|
@@ -4,6 +4,7 @@ import { ColumnViewColumnNode, ColumnViewItemNode, ColumnViewNode } from "./node
|
|
|
4
4
|
import { DropDownItemNode, DropDownNode } from "./nodes/dropdown.js";
|
|
5
5
|
import { FlowBoxNode } from "./nodes/flow-box.js";
|
|
6
6
|
import { GridChildNode, GridNode } from "./nodes/grid.js";
|
|
7
|
+
import { AdwHeaderBarNode, HeaderBarNode } from "./nodes/header-bar.js";
|
|
7
8
|
import { ListItemNode, ListViewNode } from "./nodes/list.js";
|
|
8
9
|
import { ListBoxNode } from "./nodes/list-box.js";
|
|
9
10
|
import { ApplicationMenuNode, MenuItemNode, MenuSectionNode, MenuSubmenuNode, PopoverMenuBarNode, PopoverMenuRootNode, } from "./nodes/menu.js";
|
|
@@ -14,6 +15,7 @@ import { SlotNode } from "./nodes/slot.js";
|
|
|
14
15
|
import { StackNode, StackPageNode } from "./nodes/stack.js";
|
|
15
16
|
import { TextViewNode } from "./nodes/text-view.js";
|
|
16
17
|
import { ToggleButtonNode } from "./nodes/toggle-button.js";
|
|
18
|
+
import { ToolbarViewSlotNode } from "./nodes/toolbar-view.js";
|
|
17
19
|
import { WidgetNode } from "./nodes/widget.js";
|
|
18
20
|
import { WindowNode } from "./nodes/window.js";
|
|
19
21
|
export { ROOT_NODE_CONTAINER } from "./nodes/root.js";
|
|
@@ -28,6 +30,7 @@ const VIRTUAL_NODES = [
|
|
|
28
30
|
MenuItemNode,
|
|
29
31
|
MenuSectionNode,
|
|
30
32
|
MenuSubmenuNode,
|
|
33
|
+
ToolbarViewSlotNode,
|
|
31
34
|
SlotNode,
|
|
32
35
|
];
|
|
33
36
|
const SPECIALIZED_NODES = [
|
|
@@ -50,6 +53,8 @@ const CONTAINER_NODES = [
|
|
|
50
53
|
ListViewNode,
|
|
51
54
|
NotebookNode,
|
|
52
55
|
StackNode,
|
|
56
|
+
AdwHeaderBarNode,
|
|
57
|
+
HeaderBarNode,
|
|
53
58
|
];
|
|
54
59
|
const NODE_CLASSES = [RootNode, ...VIRTUAL_NODES, ...SPECIALIZED_NODES, ...CONTAINER_NODES, WidgetNode];
|
|
55
60
|
/**
|