@gjsify/adwaita-web 0.64.12-next.280 → 0.64.12-next.281
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/lib/types/namespace/gio.d.ts +1 -0
- package/lib/types/namespace/gtk.d.ts +1 -1
- package/lib/types/shared-tree-builder.d.ts +7 -0
- package/package.json +12 -12
- package/src/gobject-door.spec.ts +57 -0
- package/src/namespace/gio.ts +3 -0
- package/src/namespace/gtk.ts +13 -9
- package/src/shared-tree-builder.ts +42 -23
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import '../file-chooser-host.js';
|
|
2
2
|
export { GioMenu as Menu, GioMenuItem as MenuItem } from '@gjsify/adwaita-core';
|
|
3
|
+
export { ListModel } from '@gjsify/adwaita-core';
|
|
3
4
|
export { GioFile as File, GioListStore as ListStore, GioOutputStream as OutputStream, GioFileOutputStream as FileOutputStream, FileCreateFlags, IOErrorEnum, promisify as _promisify, } from '@gjsify/adwaita-core';
|
|
4
5
|
export { SimpleAction, SimpleActionGroup, ApplicationFlags } from '@gjsify/adwaita-core';
|
|
5
6
|
export { GioResource as Resource, ResourceError, ResourceLookupFlags, resources_enumerate_children, resources_get_info, resources_lookup_data, resources_register, resources_unregister, } from '@gjsify/adwaita-core';
|
|
@@ -65,7 +65,7 @@ export { GtkStack as Stack } from '../elements/gtk-stack.js';
|
|
|
65
65
|
export { GtkStackPage as StackPage } from '../elements/gtk-stack-page.js';
|
|
66
66
|
export { GtkStackSidebar as StackSidebar } from '../elements/gtk-stack-sidebar.js';
|
|
67
67
|
export { GtkStackSwitcher as StackSwitcher } from '../elements/gtk-stack-switcher.js';
|
|
68
|
-
export { GtkStringList as StringList } from '@gjsify/adwaita-core';
|
|
68
|
+
export { GtkStringList as StringList, GtkStringObject as StringObject } from '@gjsify/adwaita-core';
|
|
69
69
|
export { GtkSwitch as Switch } from '../elements/gtk-switch.js';
|
|
70
70
|
export { GtkText as Text } from '../elements/gtk-text.js';
|
|
71
71
|
export { GtkTextView as TextView } from '../elements/gtk-text-view.js';
|
|
@@ -34,6 +34,8 @@ interface BuildRecord {
|
|
|
34
34
|
menus?: SharedTreeNode['menus'];
|
|
35
35
|
/** Present when the tree is the template of a registered class (ADR 0096): handlers and binds go through it. */
|
|
36
36
|
template?: TemplateScope;
|
|
37
|
+
/** A template's handler connections, made once every id exists: `object=` may name a node built later. */
|
|
38
|
+
connects?: (() => void)[];
|
|
37
39
|
}
|
|
38
40
|
/** Calls `onSize` with the host's measured size, now and on every change; returns its disposer. */
|
|
39
41
|
export type SizeSource = (element: Element, onSize: (size: BreakpointSize) => void) => () => void;
|
|
@@ -141,6 +143,11 @@ export interface BuiltTemplateTree {
|
|
|
141
143
|
* to their handlers and binds through `scope`, none appended and nothing written to `host`'s
|
|
142
144
|
* attributes, because `host` is still inside its constructor. `finish` and the caller's own append
|
|
143
145
|
* belong to the first `connectedCallback`.
|
|
146
|
+
*
|
|
147
|
+
* Objects written beside the template (`Adw.AlertDialog dialog { }`, `$Learn learn { }`) are built in
|
|
148
|
+
* the same id scope and named in `objects`, which is what `InternalChildren` and `Children` read —
|
|
149
|
+
* `gtk_widget_class_bind_template_child` reaches any object id of the template's builder. They are
|
|
150
|
+
* not in `children`: a sibling is an independent object, placed by the class or not at all.
|
|
144
151
|
*/
|
|
145
152
|
export declare function buildTemplateTree(tree: SharedTreeNode, scope: TemplateScope, host: HTMLElement): BuiltTemplateTree;
|
|
146
153
|
/** A tree {@link mountSharedTree} built and connected. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/adwaita-web",
|
|
3
|
-
"version": "0.64.12-next.
|
|
3
|
+
"version": "0.64.12-next.281",
|
|
4
4
|
"description": "Adwaita/Libadwaita web components for browser targets",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -58,14 +58,14 @@
|
|
|
58
58
|
"@codemirror/language": "^6.12.4",
|
|
59
59
|
"@codemirror/state": "^6.7.0",
|
|
60
60
|
"@codemirror/view": "^6.43.4",
|
|
61
|
-
"@gjsify/adwaita-core": "0.64.12-next.
|
|
62
|
-
"@gjsify/adwaita-fonts": "0.64.12-next.
|
|
63
|
-
"@gjsify/adwaita-icons": "0.64.12-next.
|
|
64
|
-
"@gjsify/gtksource-core": "0.64.12-next.
|
|
61
|
+
"@gjsify/adwaita-core": "0.64.12-next.281",
|
|
62
|
+
"@gjsify/adwaita-fonts": "0.64.12-next.281",
|
|
63
|
+
"@gjsify/adwaita-icons": "0.64.12-next.281",
|
|
64
|
+
"@gjsify/gtksource-core": "0.64.12-next.281",
|
|
65
65
|
"@lezer/highlight": "^1.2.3"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
|
-
"@gjsify/app-settings": "0.64.12-next.
|
|
68
|
+
"@gjsify/app-settings": "0.64.12-next.281"
|
|
69
69
|
},
|
|
70
70
|
"peerDependenciesMeta": {
|
|
71
71
|
"@gjsify/app-settings": {
|
|
@@ -73,11 +73,11 @@
|
|
|
73
73
|
}
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
|
-
"@gjsify/app-settings": "0.64.12-next.
|
|
77
|
-
"@gjsify/blueprint": "0.64.12-next.
|
|
78
|
-
"@gjsify/cli": "0.64.12-next.
|
|
79
|
-
"@gjsify/unit": "0.64.12-next.
|
|
80
|
-
"@gjsify/vite-plugin-blueprint": "0.64.12-next.
|
|
76
|
+
"@gjsify/app-settings": "0.64.12-next.281",
|
|
77
|
+
"@gjsify/blueprint": "0.64.12-next.281",
|
|
78
|
+
"@gjsify/cli": "0.64.12-next.281",
|
|
79
|
+
"@gjsify/unit": "0.64.12-next.281",
|
|
80
|
+
"@gjsify/vite-plugin-blueprint": "0.64.12-next.281",
|
|
81
81
|
"sass": "^1.101.0",
|
|
82
82
|
"typescript": "^6.0.3"
|
|
83
83
|
},
|
|
@@ -115,5 +115,5 @@
|
|
|
115
115
|
"url": "https://github.com/gjsify/gjsify/issues"
|
|
116
116
|
},
|
|
117
117
|
"homepage": "https://github.com/gjsify/gjsify/tree/main/packages/web/adwaita-web#readme",
|
|
118
|
-
"gitHead": "
|
|
118
|
+
"gitHead": "c4d2e266c0d9849860b9828c14ebfe37b6d493b8"
|
|
119
119
|
}
|
package/src/gobject-door.spec.ts
CHANGED
|
@@ -91,6 +91,45 @@ GObject.registerClass(
|
|
|
91
91
|
GoWebSensitive as never,
|
|
92
92
|
);
|
|
93
93
|
|
|
94
|
+
const SiblingsTemplate = tree(`template $GoWebSiblings : Adw.Bin {
|
|
95
|
+
child: Gtk.Box {
|
|
96
|
+
Gtk.Label caption { label: bind peer.title; }
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
Adw.AlertDialog unsavedChangesDialog { heading: "Save changes?"; }
|
|
101
|
+
|
|
102
|
+
$GoWebPeer peer { }`);
|
|
103
|
+
|
|
104
|
+
class GoWebPeer extends Adw.Bin {
|
|
105
|
+
declare title: string;
|
|
106
|
+
}
|
|
107
|
+
GObject.registerClass(
|
|
108
|
+
{
|
|
109
|
+
GTypeName: 'GoWebPeer',
|
|
110
|
+
Properties: { title: GObject.ParamSpec.string('title', '', '', GObject.ParamFlags.READWRITE, 'first') },
|
|
111
|
+
},
|
|
112
|
+
GoWebPeer as never,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
class GoWebSiblings extends Adw.Bin {
|
|
116
|
+
declare _unsavedChangesDialog: HTMLElement & { heading: string };
|
|
117
|
+
declare _peer: GoWebPeer;
|
|
118
|
+
declare _caption: HTMLElement & { label: string };
|
|
119
|
+
constructor(params?: Record<string, unknown>) {
|
|
120
|
+
// @ts-expect-error the element's own constructor takes none; the registered class's takes the params
|
|
121
|
+
super(params);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
GObject.registerClass(
|
|
125
|
+
{
|
|
126
|
+
GTypeName: 'GoWebSiblings',
|
|
127
|
+
Template: SiblingsTemplate,
|
|
128
|
+
InternalChildren: ['unsavedChangesDialog', 'peer', 'caption'],
|
|
129
|
+
},
|
|
130
|
+
GoWebSiblings as never,
|
|
131
|
+
);
|
|
132
|
+
|
|
94
133
|
export const GObjectDoorTest = async () => {
|
|
95
134
|
await driveGObjectVectors(subject, { describe, it, expect }, GOBJECT_VECTORS);
|
|
96
135
|
await describe('adwaita-web: GObject.registerClass end to end (ADR 0096)', async () => {
|
|
@@ -98,6 +137,24 @@ export const GObjectDoorTest = async () => {
|
|
|
98
137
|
expect(tagForTypeName('Hexdump')).toBe('gjsify-hexdump');
|
|
99
138
|
expect(customElements.get('gjsify-go-web-hexdump')).toBe(GoWebHexdump);
|
|
100
139
|
});
|
|
140
|
+
await it('reaches the objects written beside the template through InternalChildren, outside its children', () => {
|
|
141
|
+
const el = new GoWebSiblings();
|
|
142
|
+
expect(el._unsavedChangesDialog.localName).toBe('adw-alert-dialog');
|
|
143
|
+
expect(el._peer instanceof GoWebPeer).toBe(true);
|
|
144
|
+
document.body.append(el);
|
|
145
|
+
try {
|
|
146
|
+
// Independent objects, as in GTK: neither is a child of the template widget.
|
|
147
|
+
expect(el.contains(el._unsavedChangesDialog)).toBe(false);
|
|
148
|
+
expect(el.contains(el._peer)).toBe(false);
|
|
149
|
+
expect(el.contains(el._caption)).toBe(true);
|
|
150
|
+
// A template child names a sibling by id, and follows it.
|
|
151
|
+
expect(el._caption.label).toBe('first');
|
|
152
|
+
el._peer.title = 'second';
|
|
153
|
+
expect(el._caption.label).toBe('second');
|
|
154
|
+
} finally {
|
|
155
|
+
el.remove();
|
|
156
|
+
}
|
|
157
|
+
});
|
|
101
158
|
await it('refuses a second class under the same tag', () => {
|
|
102
159
|
expect(() =>
|
|
103
160
|
GObject.registerClass({ GTypeName: 'GoWebHexdump' }, class extends Adw.Bin {} as never),
|
package/src/namespace/gio.ts
CHANGED
|
@@ -29,6 +29,9 @@ import { createAppInfo } from '@gjsify/adwaita-core';
|
|
|
29
29
|
import { webPlatformHost } from '../platform-host.js';
|
|
30
30
|
|
|
31
31
|
export { GioMenu as Menu, GioMenuItem as MenuItem } from '@gjsify/adwaita-core';
|
|
32
|
+
// ADR 0122 § 2: the interface a `Gtk.StringList` implements and an `Implements: [Gio.ListModel]`
|
|
33
|
+
// names. It is a token, not a class — the whole of the subset's interface support.
|
|
34
|
+
export { ListModel } from '@gjsify/adwaita-core';
|
|
32
35
|
export {
|
|
33
36
|
GioFile as File,
|
|
34
37
|
GioListStore as ListStore,
|
package/src/namespace/gtk.ts
CHANGED
|
@@ -11,14 +11,18 @@
|
|
|
11
11
|
// which is what `<adw-radio>`'s own `why` in the ledger says. So the plain form takes the
|
|
12
12
|
// GIR name and the grouped one keeps its flat export in `src/index.ts`.
|
|
13
13
|
//
|
|
14
|
-
//
|
|
15
|
-
// `adjustment` and a `model` property take.
|
|
16
|
-
// clause-2 namespace carry them,
|
|
17
|
-
// where each one is declared, and the
|
|
18
|
-
// `generated/value-types.mts`. They are
|
|
19
|
-
// where the portable list and adjustment
|
|
20
|
-
//
|
|
21
|
-
//
|
|
14
|
+
// THREE MEMBERS HERE ARE NOT WIDGETS — `Gtk.Adjustment`, `Gtk.StringList` and the
|
|
15
|
+
// `Gtk.StringObject` the list holds, the values an `adjustment` and a `model` property take.
|
|
16
|
+
// ADR 0034 § Amendment 19 is what lets a clause-2 namespace carry them,
|
|
17
|
+
// `CONSTRUCTIBLE_VALUES` in `scripts/value-types.mjs` is where each one is declared, and the
|
|
18
|
+
// GIR answers for all three in gtk-host's committed `generated/value-types.mts`. They are
|
|
19
|
+
// re-exported from `@gjsify/adwaita-core`, which is where the portable list and adjustment
|
|
20
|
+
// already live.
|
|
21
|
+
//
|
|
22
|
+
// `Gtk.StringList` IS A LIST MODEL, NOT AN ARRAY (ADR 0122). The first version of it was an
|
|
23
|
+
// `Array` subclass, which made `model: ['a','b']` and `model: new Gtk.StringList(…)` the same
|
|
24
|
+
// write; it is now what GTK ships — `Implements: [Gio.ListModel]`, items that are
|
|
25
|
+
// `Gtk.StringObject`s, read by `get_n_items` / `get_item` and never by `length` or `[0]`.
|
|
22
26
|
|
|
23
27
|
export { GtkAdjustment as Adjustment } from '@gjsify/adwaita-core';
|
|
24
28
|
// ADR 0100: the file dialog is no element either — `present`-less, it asks the driver
|
|
@@ -89,7 +93,7 @@ export { GtkStack as Stack } from '../elements/gtk-stack.js';
|
|
|
89
93
|
export { GtkStackPage as StackPage } from '../elements/gtk-stack-page.js';
|
|
90
94
|
export { GtkStackSidebar as StackSidebar } from '../elements/gtk-stack-sidebar.js';
|
|
91
95
|
export { GtkStackSwitcher as StackSwitcher } from '../elements/gtk-stack-switcher.js';
|
|
92
|
-
export { GtkStringList as StringList } from '@gjsify/adwaita-core';
|
|
96
|
+
export { GtkStringList as StringList, GtkStringObject as StringObject } from '@gjsify/adwaita-core';
|
|
93
97
|
export { GtkSwitch as Switch } from '../elements/gtk-switch.js';
|
|
94
98
|
export { GtkText as Text } from '../elements/gtk-text.js';
|
|
95
99
|
export { GtkTextView as TextView } from '../elements/gtk-text-view.js';
|
|
@@ -101,6 +101,8 @@ interface BuildRecord {
|
|
|
101
101
|
menus?: SharedTreeNode['menus'];
|
|
102
102
|
/** Present when the tree is the template of a registered class (ADR 0096): handlers and binds go through it. */
|
|
103
103
|
template?: TemplateScope;
|
|
104
|
+
/** A template's handler connections, made once every id exists: `object=` may name a node built later. */
|
|
105
|
+
connects?: (() => void)[];
|
|
104
106
|
}
|
|
105
107
|
|
|
106
108
|
/** Calls `onSize` with the host's measured size, now and on every change; returns its disposer. */
|
|
@@ -229,13 +231,8 @@ function refuseSiblings(node: SharedTreeNode, door: string): void {
|
|
|
229
231
|
);
|
|
230
232
|
}
|
|
231
233
|
|
|
232
|
-
/**
|
|
233
|
-
function
|
|
234
|
-
// ADR 0093 § 2: the whole tree is checked against the capability table before an element is
|
|
235
|
-
// created. A refused `layout` is named with its node and the reason the table gives.
|
|
236
|
-
assertTreeConstructs('adwaita-web', capabilities, node, Object.keys(ARIA_OF));
|
|
237
|
-
if (node.menus !== undefined) record.menus = node.menus;
|
|
238
|
-
const root = buildNode(node, record);
|
|
234
|
+
/** The sibling objects of `node` built in `record`'s id scope, by id; each has to have one, or nothing could ask for it. */
|
|
235
|
+
function buildSiblings(node: SharedTreeNode, record: BuildRecord): Map<string, HTMLElement> {
|
|
239
236
|
const siblings = new Map<string, HTMLElement>();
|
|
240
237
|
for (const sibling of node.siblings ?? []) {
|
|
241
238
|
if (sibling.id === undefined) {
|
|
@@ -245,6 +242,17 @@ function buildTree(node: SharedTreeNode, record: BuildRecord): BuiltTree {
|
|
|
245
242
|
}
|
|
246
243
|
siblings.set(sibling.id, buildNode(sibling, record));
|
|
247
244
|
}
|
|
245
|
+
return siblings;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** Every node, its siblings in the same id scope, then the binds and breakpoints that name them. */
|
|
249
|
+
function buildTree(node: SharedTreeNode, record: BuildRecord): BuiltTree {
|
|
250
|
+
// ADR 0093 § 2: the whole tree is checked against the capability table before an element is
|
|
251
|
+
// created. A refused `layout` is named with its node and the reason the table gives.
|
|
252
|
+
assertTreeConstructs('adwaita-web', capabilities, node, Object.keys(ARIA_OF));
|
|
253
|
+
if (node.menus !== undefined) record.menus = node.menus;
|
|
254
|
+
const root = buildNode(node, record);
|
|
255
|
+
const siblings = buildSiblings(node, record);
|
|
248
256
|
for (const bind of record.binds) bindProperty(bind, record);
|
|
249
257
|
for (const host of record.breakpointHosts) bindBreakpoints(host.el, host.node, record);
|
|
250
258
|
return { root, siblings, objects: new Map(record.ids) };
|
|
@@ -265,7 +273,7 @@ function newRecord(
|
|
|
265
273
|
disposers: [],
|
|
266
274
|
...(scope === undefined ? {} : { scope }),
|
|
267
275
|
...(observeSize === undefined ? {} : { observeSize }),
|
|
268
|
-
...(template === undefined ? {} : { template }),
|
|
276
|
+
...(template === undefined ? {} : { template, connects: [] }),
|
|
269
277
|
};
|
|
270
278
|
}
|
|
271
279
|
|
|
@@ -606,9 +614,13 @@ export interface BuiltTemplateTree {
|
|
|
606
614
|
* to their handlers and binds through `scope`, none appended and nothing written to `host`'s
|
|
607
615
|
* attributes, because `host` is still inside its constructor. `finish` and the caller's own append
|
|
608
616
|
* belong to the first `connectedCallback`.
|
|
617
|
+
*
|
|
618
|
+
* Objects written beside the template (`Adw.AlertDialog dialog { }`, `$Learn learn { }`) are built in
|
|
619
|
+
* the same id scope and named in `objects`, which is what `InternalChildren` and `Children` read —
|
|
620
|
+
* `gtk_widget_class_bind_template_child` reaches any object id of the template's builder. They are
|
|
621
|
+
* not in `children`: a sibling is an independent object, placed by the class or not at all.
|
|
609
622
|
*/
|
|
610
623
|
export function buildTemplateTree(tree: SharedTreeNode, scope: TemplateScope, host: HTMLElement): BuiltTemplateTree {
|
|
611
|
-
refuseSiblings(tree, 'buildTemplateTree');
|
|
612
624
|
assertTreeConstructs('adwaita-web', capabilities, tree, Object.keys(ARIA_OF));
|
|
613
625
|
const record = newRecord(undefined, undefined, scope);
|
|
614
626
|
record.menus = tree.menus;
|
|
@@ -616,6 +628,8 @@ export function buildTemplateTree(tree: SharedTreeNode, scope: TemplateScope, ho
|
|
|
616
628
|
collectNode(host, tree, record);
|
|
617
629
|
bindSignals(host, tree, record);
|
|
618
630
|
const children = buildChildren(host, tree, record);
|
|
631
|
+
buildSiblings(tree, record);
|
|
632
|
+
for (const connect of record.connects ?? []) connect();
|
|
619
633
|
for (const bind of record.binds) bindProperty(bind, record);
|
|
620
634
|
for (const breakpointHost of record.breakpointHosts) {
|
|
621
635
|
bindBreakpoints(breakpointHost.el, breakpointHost.node, record);
|
|
@@ -648,21 +662,26 @@ function bindSignals(el: HTMLElement, node: SharedTreeNode, record: BuildRecord)
|
|
|
648
662
|
`the handler '${signal.handler}' for '${name}' uses after, which is not in the GObject subset.`,
|
|
649
663
|
);
|
|
650
664
|
}
|
|
651
|
-
const
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
665
|
+
const template = record.template;
|
|
666
|
+
// `object=` is resolved when the whole tree exists, as `gtk_builder_connect_signals` does: it may
|
|
667
|
+
// name a node built after this one, or a sibling object written beside the template.
|
|
668
|
+
record.connects!.push(() => {
|
|
669
|
+
const object =
|
|
670
|
+
signal.object === undefined
|
|
671
|
+
? undefined
|
|
672
|
+
: signal.object === 'template'
|
|
673
|
+
? template.instance
|
|
674
|
+
: record.ids.get(signal.object);
|
|
675
|
+
if (signal.object !== undefined && object === undefined) {
|
|
676
|
+
throw new Error(
|
|
677
|
+
`the handler '${signal.handler}' for '${name}' names the object '${signal.object}', which nothing in this tree has.`,
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
(el as unknown as GObjectInstance).connect(
|
|
681
|
+
name,
|
|
682
|
+
template.handler(signal.handler, { object, flags: signal.flags }),
|
|
660
683
|
);
|
|
661
|
-
}
|
|
662
|
-
(el as unknown as GObjectInstance).connect(
|
|
663
|
-
name,
|
|
664
|
-
record.template.handler(signal.handler, { object, flags: signal.flags }),
|
|
665
|
-
);
|
|
684
|
+
});
|
|
666
685
|
continue;
|
|
667
686
|
}
|
|
668
687
|
if (signal.object !== undefined || (signal.flags?.length ?? 0) > 0) {
|