@gtkx/react 1.5.0 → 1.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.
@@ -1,5 +1,5 @@
1
- import type * as Gtk from "@gtkx/gi/gtk";
2
1
  import * as Adw from "@gtkx/gi/adw";
2
+ import * as Gtk from "@gtkx/gi/gtk";
3
3
  import type { AlertDialogResponse } from "./prop-types.js";
4
4
  import {
5
5
  applicationCreator,
@@ -35,22 +35,27 @@ type PrefixSuffixRow = Adw.ActionRow | Adw.EntryRow | Adw.ExpanderRow;
35
35
 
36
36
  const SLOT_SUFFIX = "Slot";
37
37
  const childSetter = childSetterSlot<AdwChildSetter>();
38
- const contentSetter = contentSetterSlot<AdwContentSetter>();
39
- const breakpoints = methodSlot<BreakpointHost, Adw.Breakpoint>("breakpoints", "AdwBreakpoint", "addBreakpoint");
38
+ const contentSetter = contentSetterSlot<AdwContentSetter>(Gtk.Widget);
39
+ const breakpoints = methodSlot<BreakpointHost, Adw.Breakpoint>("breakpoints", Adw.Breakpoint, "addBreakpoint");
40
40
 
41
41
  const prefixSuffix = [
42
- methodSlot<PrefixSuffixRow, Gtk.Widget>("prefix", "GtkWidget", "addPrefix", "remove"),
43
- methodSlot<PrefixSuffixRow, Gtk.Widget>("suffix", "GtkWidget", "addSuffix", "remove"),
42
+ methodSlot<PrefixSuffixRow, Gtk.Widget>("prefix", Gtk.Widget, "addPrefix", "remove"),
43
+ methodSlot<PrefixSuffixRow, Gtk.Widget>("suffix", Gtk.Widget, "addSuffix", "remove"),
44
44
  ];
45
45
 
46
46
  const preferencesDialogChildren = methodSlot<Adw.PreferencesDialog, Adw.PreferencesPage>(
47
- "children", "AdwPreferencesPage", "add", "remove",
47
+ "children", Adw.PreferencesPage, "add", "remove",
48
48
  );
49
49
 
50
- const alertDialogExtraChild = setterSlot<Adw.AlertDialog, Gtk.Widget>("children", "GtkWidget", "setExtraChild");
51
- const sidebarSections = indexedSlot<Adw.Sidebar, Adw.SidebarSection>("children", "AdwSidebarSection");
52
- const sidebarItems = indexedSlot<Adw.SidebarSection, Adw.SidebarItem>("children", "AdwSidebarItem");
53
- const isWidget = childMatcher("GtkWidget");
50
+ const alertDialogExtraChild = setterSlot<Adw.AlertDialog, Gtk.Widget>("children", Gtk.Widget, "setExtraChild");
51
+ const sidebarSections = indexedSlot<Adw.Sidebar, Adw.SidebarSection>("children", Adw.SidebarSection);
52
+ const sidebarItems = indexedSlot<Adw.SidebarSection, Adw.SidebarItem>("children", Adw.SidebarItem);
53
+ const isWidget = childMatcher(Gtk.Widget);
54
+
55
+ const scrollableWidget = {
56
+ [Symbol.hasInstance]: (value: unknown): value is Gtk.Scrollable & Gtk.Widget =>
57
+ value instanceof Gtk.Scrollable && value instanceof Gtk.Widget,
58
+ };
54
59
 
55
60
  const multiLayoutSlots: ElementBehavior<Adw.MultiLayoutView> = {
56
61
  attach: (view, child, info) => {
@@ -60,7 +65,7 @@ const multiLayoutSlots: ElementBehavior<Adw.MultiLayoutView> = {
60
65
  return;
61
66
  }
62
67
 
63
- view.setChild(id, child as Gtk.Widget);
68
+ view.setChild(id, child);
64
69
 
65
70
  return true;
66
71
  },
@@ -71,15 +76,13 @@ const multiLayoutSlots: ElementBehavior<Adw.MultiLayoutView> = {
71
76
  return;
72
77
  }
73
78
 
74
- const widget = child as Gtk.Widget;
75
-
76
- if (view.getChild(id) === widget) {
77
- widget.unparent();
79
+ if (view.getChild(id) === child) {
80
+ child.unparent();
78
81
  }
79
82
  },
80
83
  };
81
84
 
82
- const multiLayoutLayouts = slot<Adw.MultiLayoutView, Gtk.Widget>("layouts", "GtkWidget", {
85
+ const multiLayoutLayouts = slot<Adw.MultiLayoutView, Gtk.Widget>("layouts", Gtk.Widget, {
83
86
  attach: (view, content) => {
84
87
  const layout = new Adw.Layout({ content });
85
88
  view.addLayout(layout);
@@ -113,7 +116,7 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
113
116
  },
114
117
  AdwClampScrollable: {
115
118
  behaviors: [
116
- setterSlot<Adw.ClampScrollable, Gtk.Scrollable & Gtk.Widget>("children", "GtkScrollable", "setChild"),
119
+ setterSlot<Adw.ClampScrollable, Gtk.Scrollable & Gtk.Widget>("children", scrollableWidget, "setChild"),
117
120
  ],
118
121
  },
119
122
  ...forTypes(CHILD_SETTER_TYPES, {
@@ -135,7 +138,7 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
135
138
  behaviors: [
136
139
  childSetter,
137
140
  methodSlot<Adw.BreakpointBin, Adw.Breakpoint>(
138
- "breakpoints", "AdwBreakpoint", "addBreakpoint", "removeBreakpoint",
141
+ "breakpoints", Adw.Breakpoint, "addBreakpoint", "removeBreakpoint",
139
142
  ),
140
143
  ],
141
144
  },
@@ -148,18 +151,18 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
148
151
  AdwExpanderRow: {
149
152
  behaviors: [
150
153
  ...prefixSuffix,
151
- methodSlot<Adw.ExpanderRow, Gtk.Widget>("rows", "GtkWidget", "addRow", "remove"),
154
+ methodSlot<Adw.ExpanderRow, Gtk.Widget>("rows", Gtk.Widget, "addRow", "remove"),
152
155
  ],
153
156
  },
154
157
  AdwNavigationSplitView: {
155
- behaviors: [contentSetterSlot<Adw.NavigationSplitView, Adw.NavigationPage>("AdwNavigationPage")],
158
+ behaviors: [contentSetterSlot<Adw.NavigationSplitView, Adw.NavigationPage>(Adw.NavigationPage)],
156
159
  },
157
160
  AdwWrapBox: {
158
161
  behaviors: [boxSlot<Adw.WrapBox>()],
159
162
  },
160
163
  AdwCarousel: {
161
164
  behaviors: [
162
- slot<Adw.Carousel, Gtk.Widget>("children", "GtkWidget", {
165
+ slot<Adw.Carousel, Gtk.Widget>("children", Gtk.Widget, {
163
166
  attach: (carousel, child, info) => {
164
167
  carousel.insert(child, info.index);
165
168
  },
@@ -174,7 +177,7 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
174
177
  },
175
178
  AdwPreferencesPage: {
176
179
  behaviors: [
177
- slot<Adw.PreferencesPage, Adw.PreferencesGroup>("children", "AdwPreferencesGroup", {
180
+ slot<Adw.PreferencesPage, Adw.PreferencesGroup>("children", Adw.PreferencesGroup, {
178
181
  attach: (page, group, info) => {
179
182
  page.insert(group, info.index);
180
183
  },
@@ -188,11 +191,11 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
188
191
  behaviors: [preferencesDialogChildren],
189
192
  },
190
193
  AdwPreferencesGroup: {
191
- behaviors: [methodSlot<Adw.PreferencesGroup, Gtk.Widget>("children", "GtkWidget", "add", "remove")],
194
+ behaviors: [methodSlot<Adw.PreferencesGroup, Gtk.Widget>("children", Gtk.Widget, "add", "remove")],
192
195
  },
193
196
  AdwTabView: {
194
197
  behaviors: [
195
- slot<Adw.TabView, Gtk.Widget>("children", "GtkWidget", {
198
+ slot<Adw.TabView, Gtk.Widget>("children", Gtk.Widget, {
196
199
  attach: (view, child, info) => view.insert(child, info.index),
197
200
  reorder: (view, _child, info) => {
198
201
  if (info.adopted instanceof Adw.TabPage) {
@@ -210,37 +213,37 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
210
213
  },
211
214
  AdwNavigationView: {
212
215
  behaviors: [
213
- methodSlot<Adw.NavigationView, Adw.NavigationPage>("children", "AdwNavigationPage", "add", "remove"),
216
+ methodSlot<Adw.NavigationView, Adw.NavigationPage>("children", Adw.NavigationPage, "add", "remove"),
214
217
  ],
215
218
  },
216
219
  AdwViewStack: {
217
220
  behaviors: [
218
- methodSlot<Adw.ViewStack, Gtk.Widget>("children", "GtkWidget", "add", "remove"),
221
+ methodSlot<Adw.ViewStack, Gtk.Widget>("children", Gtk.Widget, "add", "remove"),
219
222
  deferred<Adw.ViewStack, string>("visibleChildName", (stack, name) => stack.getChildByName(name) !== null),
220
223
  ],
221
224
  },
222
225
  AdwToolbarView: {
223
226
  behaviors: [
224
- contentSetterSlot<Adw.ToolbarView>(),
225
- methodSlot<Adw.ToolbarView, Gtk.Widget>("topBar", "GtkWidget", "addTopBar", "remove"),
226
- methodSlot<Adw.ToolbarView, Gtk.Widget>("bottomBar", "GtkWidget", "addBottomBar", "remove"),
227
+ contentSetterSlot<Adw.ToolbarView>(Gtk.Widget),
228
+ methodSlot<Adw.ToolbarView, Gtk.Widget>("topBar", Gtk.Widget, "addTopBar", "remove"),
229
+ methodSlot<Adw.ToolbarView, Gtk.Widget>("bottomBar", Gtk.Widget, "addBottomBar", "remove"),
227
230
  ],
228
231
  },
229
232
  AdwHeaderBar: {
230
233
  behaviors: [
231
- methodSlot<Adw.HeaderBar, Gtk.Widget>("start", "GtkWidget", "packStart", "remove"),
232
- methodSlot<Adw.HeaderBar, Gtk.Widget>("end", "GtkWidget", "packEnd", "remove"),
234
+ methodSlot<Adw.HeaderBar, Gtk.Widget>("start", Gtk.Widget, "packStart", "remove"),
235
+ methodSlot<Adw.HeaderBar, Gtk.Widget>("end", Gtk.Widget, "packEnd", "remove"),
233
236
  ],
234
237
  },
235
238
  AdwShortcutsDialog: {
236
- behaviors: [methodSlot<Adw.ShortcutsDialog, Adw.ShortcutsSection>("children", "AdwShortcutsSection", "add")],
239
+ behaviors: [methodSlot<Adw.ShortcutsDialog, Adw.ShortcutsSection>("children", Adw.ShortcutsSection, "add")],
237
240
  },
238
241
  AdwShortcutsSection: {
239
- behaviors: [methodSlot<Adw.ShortcutsSection, Adw.ShortcutsItem>("children", "AdwShortcutsItem", "add")],
242
+ behaviors: [methodSlot<Adw.ShortcutsSection, Adw.ShortcutsItem>("children", Adw.ShortcutsItem, "add")],
240
243
  },
241
244
  AdwToggleGroup: {
242
245
  behaviors: [
243
- methodSlot<Adw.ToggleGroup, Adw.Toggle>("children", "AdwToggle", "add", "remove"),
246
+ methodSlot<Adw.ToggleGroup, Adw.Toggle>("children", Adw.Toggle, "add", "remove"),
244
247
  deferred<Adw.ToggleGroup, string>("activeName", (group, name) => group.getToggleByName(name) !== null),
245
248
  deferred("active"),
246
249
  ],
@@ -72,11 +72,19 @@ const buildElement = (typeName: string, record: Props): ReactElement => {
72
72
  * Left out, the component takes `unknown`, which accepts no attributes at all.
73
73
  *
74
74
  * @param typeName GType name to render, such as `GtkButton` or the `typeName` given to `registerClass`.
75
+ * @param cls Wrapper class the name resolves to. Referencing it keeps the class, and with it the
76
+ * registration the reconciler's name lookup depends on, in a tree-shaken bundle; the component
77
+ * itself renders through the name.
78
+ * @param metadata Registered metadata entry for the type. Referencing it keeps the type's property
79
+ * and signal tables, which register themselves when evaluated, in a tree-shaken bundle.
75
80
  * @returns A component taking that type's props.
76
81
  */
77
82
  /* eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- callers name the props */
78
- function createElementComponent<P = unknown>(typeName: string): (props: P) => ReactNode {
79
- return (props: P): ReactNode => buildElement(typeName, isRecord(props) ? props : {});
80
- }
83
+ const createElementComponent: <P = unknown>(
84
+ typeName: string,
85
+ cls?: unknown,
86
+ metadata?: unknown,
87
+ ) => (props: P) => ReactNode =
88
+ (typeName) => (props): ReactNode => buildElement(typeName, isRecord(props) ? props : {});
81
89
 
82
90
  export { Prop, createElementComponent };
@@ -47,8 +47,8 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
47
47
  }),
48
48
  ...forTypes(["GtkHeaderBar", "GtkActionBar"], {
49
49
  behaviors: [
50
- methodSlot<Gtk.HeaderBar | Gtk.ActionBar, Gtk.Widget>("start", "GtkWidget", "packStart", "remove"),
51
- methodSlot<Gtk.HeaderBar | Gtk.ActionBar, Gtk.Widget>("end", "GtkWidget", "packEnd", "remove"),
50
+ methodSlot<Gtk.HeaderBar | Gtk.ActionBar, Gtk.Widget>("start", Gtk.Widget, "packStart", "remove"),
51
+ methodSlot<Gtk.HeaderBar | Gtk.ActionBar, Gtk.Widget>("end", Gtk.Widget, "packEnd", "remove"),
52
52
  ],
53
53
  }),
54
54
  GtkWindow: {
@@ -56,7 +56,7 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
56
56
  },
57
57
  GtkWidget: {
58
58
  behaviors: [
59
- slot<Gtk.Widget, Gtk.Popover>("children", "GtkPopover", {
59
+ slot<Gtk.Widget, Gtk.Popover>("children", Gtk.Popover, {
60
60
  attach: (parent, popover) => {
61
61
  popover.setParent(parent);
62
62
  },
@@ -65,10 +65,10 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
65
65
  },
66
66
  }),
67
67
  methodSlot<Gtk.Widget, Gtk.EventController>(
68
- "controllers", "GtkEventController", "addController", "removeController",
68
+ "controllers", Gtk.EventController, "addController", "removeController",
69
69
  ),
70
- setterSlot<Gtk.Widget, Gtk.LayoutManager>("layoutManager", "GtkLayoutManager", "setLayoutManager"),
71
- slot<Gtk.Widget, Gio.ActionGroup>("actionGroups", "GActionGroup", {
70
+ setterSlot<Gtk.Widget, Gtk.LayoutManager>("layoutManager", Gtk.LayoutManager, "setLayoutManager"),
71
+ slot<Gtk.Widget, Gio.ActionGroup>("actionGroups", Gio.ActionGroup, {
72
72
  attach: (widget, group, info) => {
73
73
  widget.insertActionGroup((info.props.prefix as string | null) ?? "", group);
74
74
  },
@@ -100,7 +100,7 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
100
100
  GtkOverlay: {
101
101
  behaviors: [
102
102
  childSetterSlot<Gtk.Overlay>(),
103
- slot<Gtk.Overlay, Gtk.Widget>("overlays", "GtkWidget", {
103
+ slot<Gtk.Overlay, Gtk.Widget>("overlays", Gtk.Widget, {
104
104
  attach: (overlay, child) => {
105
105
  overlay.addOverlay(child);
106
106
  },
@@ -114,7 +114,7 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
114
114
  GtkShortcutController: {
115
115
  behaviors: [
116
116
  methodSlot<Gtk.ShortcutController, Gtk.Shortcut>(
117
- "shortcuts", "GtkShortcut", "addShortcut", "removeShortcut",
117
+ "shortcuts", Gtk.Shortcut, "addShortcut", "removeShortcut",
118
118
  ),
119
119
  ],
120
120
  },
@@ -122,11 +122,11 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
122
122
  behaviors: [{ create: () => Gtk.TextChildAnchor.new() }],
123
123
  },
124
124
  GtkTextView: {
125
- behaviors: [setterSlot<Gtk.TextView, Gtk.TextBuffer>("children", "GtkTextBuffer", "setBuffer")],
125
+ behaviors: [setterSlot<Gtk.TextView, Gtk.TextBuffer>("children", Gtk.TextBuffer, "setBuffer")],
126
126
  },
127
127
  GActionMap: {
128
128
  behaviors: [
129
- slot<Gio.ActionMap, Gio.Action>("actions", "GAction", {
129
+ slot<Gio.ActionMap, Gio.Action>("actions", Gio.Action, {
130
130
  attach: (map, action) => {
131
131
  map.addAction(action);
132
132
  },
@@ -150,7 +150,7 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
150
150
  },
151
151
  GtkColumnView: {
152
152
  behaviors: [
153
- slot<Gtk.ColumnView, Gtk.ColumnViewColumn>("children", "GtkColumnViewColumn", {
153
+ slot<Gtk.ColumnView, Gtk.ColumnViewColumn>("children", Gtk.ColumnViewColumn, {
154
154
  attach: (view, column, info) => {
155
155
  view.insertColumn(info.index, column);
156
156
  },
@@ -162,7 +162,7 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
162
162
  },
163
163
  GtkGrid: {
164
164
  behaviors: [
165
- slot<Gtk.Grid, Gtk.Widget>("children", "GtkWidget", {
165
+ slot<Gtk.Grid, Gtk.Widget>("children", Gtk.Widget, {
166
166
  attach: (grid, child) => {
167
167
  grid.attach(child, 0, 0, 1, 1);
168
168
  },
@@ -175,7 +175,7 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
175
175
  },
176
176
  GtkFixed: {
177
177
  behaviors: [
178
- slot<Gtk.Fixed, Gtk.Widget>("children", "GtkWidget", {
178
+ slot<Gtk.Fixed, Gtk.Widget>("children", Gtk.Widget, {
179
179
  attach: (fixed, child) => {
180
180
  fixed.put(child, 0, 0);
181
181
  },
@@ -201,10 +201,10 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
201
201
  GtkConstraintLayout: {
202
202
  behaviors: [
203
203
  methodSlot<Gtk.ConstraintLayout, Gtk.Constraint>(
204
- "constraints", "GtkConstraint", "addConstraint", "removeConstraint",
204
+ "constraints", Gtk.Constraint, "addConstraint", "removeConstraint",
205
205
  ),
206
206
  methodSlot<Gtk.ConstraintLayout, Gtk.ConstraintGuide>(
207
- "guides", "GtkConstraintGuide", "addGuide", "removeGuide",
207
+ "guides", Gtk.ConstraintGuide, "addGuide", "removeGuide",
208
208
  ),
209
209
  list<Gtk.ConstraintLayout, VflConstraints, Gtk.Constraint[]>("vfl", {
210
210
  add: (layout, item) => [
@@ -225,13 +225,13 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
225
225
  },
226
226
  GtkStack: {
227
227
  behaviors: [
228
- methodSlot<Gtk.Stack, Gtk.Widget>("children", "GtkWidget", "addChild", "remove"),
228
+ methodSlot<Gtk.Stack, Gtk.Widget>("children", Gtk.Widget, "addChild", "remove"),
229
229
  deferred<Gtk.Stack, string>("visibleChildName", (stack, name) => stack.getChildByName(name) !== null),
230
230
  ],
231
231
  },
232
232
  GtkNotebook: {
233
233
  behaviors: [
234
- slot<Gtk.Notebook, Gtk.Widget>("children", "GtkWidget", {
234
+ slot<Gtk.Notebook, Gtk.Widget>("children", Gtk.Widget, {
235
235
  attach: (notebook, child, info) => notebook.insertPage(child, null, info.index),
236
236
  reorder: (notebook, child, info) => {
237
237
  notebook.reorderChild(child, info.index);
@@ -246,7 +246,7 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
246
246
  GtkApplication: {
247
247
  behaviors: [
248
248
  applicationCreator(Gtk.Application),
249
- methodSlot<Gtk.Application, Gtk.Window>("children", "GtkWindow", "addWindow", "removeWindow"),
249
+ methodSlot<Gtk.Application, Gtk.Window>("children", Gtk.Window, "addWindow", "removeWindow"),
250
250
  list<Gtk.Application, ActionAccel>("actionAccels", {
251
251
  add: (application, item) => {
252
252
  application.setAccelsForAction(item.detailedActionName, item.accels);
@@ -1,16 +1,15 @@
1
1
  import type * as GObject from "@gtkx/gi/gobject";
2
- import type * as Gtk from "@gtkx/gi/gtk";
2
+ import * as Gtk from "@gtkx/gi/gtk";
3
3
  import {
4
- type AnyClass,
5
4
  type ApplicationClass,
6
5
  type CommandLineApplication,
7
6
  createApplication,
8
7
  getClassType,
8
+ getInstanceType,
9
9
  TYPE_INVALID,
10
- typeFromName,
11
10
  typeIsA,
12
11
  } from "@gtkx/runtime";
13
- import { getOrInsert, isDeepEqual, kebabCase, structuredClone, unsanitizeIdentifier } from "@gtkx/utils";
12
+ import { isDeepEqual, kebabCase, structuredClone, unsanitizeIdentifier } from "@gtkx/utils";
14
13
  import type { DetachInfo, ElementBehavior, PlaceInfo, Props } from "./registry.js";
15
14
  import { getPropertyName } from "./metadata.js";
16
15
  import { applyWrite } from "./signals.js";
@@ -67,22 +66,32 @@ type IndexedChildHost<C extends GObject.Object> = GObject.Object & {
67
66
  insert: (child: C, position: number) => unknown;
68
67
  };
69
68
 
70
- const childTypeCache: Map<string, bigint> = new Map();
69
+ type ChildClass<C extends GObject.Object> =
70
+ (abstract new (...args: never[]) => C) |
71
+ { [Symbol.hasInstance]: (value: unknown) => value is C };
71
72
 
72
- const childTypeFor = (name: string): bigint => getOrInsert(childTypeCache, name, typeFromName);
73
+ const childClassType = (cls: ChildClass<GObject.Object>): bigint =>
74
+ typeof cls === "function" ? getClassType(cls) : TYPE_INVALID;
73
75
 
74
- const childMatcher =
75
- (name: string): ((child: GObject.Object) => boolean) =>
76
- (child) => {
77
- const type = childTypeFor(name);
76
+ const isChildInstance = <C extends GObject.Object>(child: GObject.Object, cls: ChildClass<C>): child is C => {
77
+ if (child instanceof cls) {
78
+ return true;
79
+ }
78
80
 
79
- return type === TYPE_INVALID || typeIsA(getClassType(child.constructor as AnyClass), type);
80
- };
81
+ const type = childClassType(cls);
82
+
83
+ return type !== TYPE_INVALID && typeIsA(getInstanceType(child), type);
84
+ };
85
+
86
+ const childMatcher =
87
+ <C extends GObject.Object>(cls: ChildClass<C> | undefined): ((child: GObject.Object) => child is C) =>
88
+ (child): child is C =>
89
+ cls !== undefined && isChildInstance(child, cls);
81
90
 
82
91
  const slotAttach =
83
92
  <P extends GObject.Object, C extends GObject.Object>(
84
93
  slotName: string,
85
- isMatch: (child: GObject.Object) => boolean,
94
+ isMatch: (child: GObject.Object) => child is C,
86
95
  attach: SlotHooks<P, C>["attach"],
87
96
  ): NonNullable<ElementBehavior["attach"]> =>
88
97
  (object, child, info) => {
@@ -90,15 +99,15 @@ const slotAttach =
90
99
  return;
91
100
  }
92
101
 
93
- return attach(object as P, child as C, info) ?? true;
102
+ return attach(object as P, child, info) ?? true;
94
103
  };
95
104
 
96
105
  const slot = <P extends GObject.Object, C extends GObject.Object>(
97
106
  slotName: string,
98
- childType: string,
107
+ childClass: ChildClass<C> | undefined,
99
108
  hooks: SlotHooks<P, C>,
100
109
  ): ElementBehavior => {
101
- const matches = childMatcher(childType);
110
+ const matches = childMatcher(childClass);
102
111
  const { attach, detach, reorder, resolve } = hooks;
103
112
  const behavior: ElementBehavior = { attach: slotAttach(slotName, matches, attach) };
104
113
 
@@ -296,7 +305,7 @@ const controlledText = (prop: string): ElementBehavior =>
296
305
  });
297
306
 
298
307
  const childSetterSlot = <P extends ChildSetter>(): ElementBehavior<P> =>
299
- slot<P, Gtk.Widget>("children", "GtkWidget", {
308
+ slot<P, Gtk.Widget>("children", Gtk.Widget, {
300
309
  attach: (parent, child) => {
301
310
  parent.setChild(child);
302
311
  },
@@ -306,9 +315,9 @@ const childSetterSlot = <P extends ChildSetter>(): ElementBehavior<P> =>
306
315
  });
307
316
 
308
317
  const contentSetterSlot = <P extends ContentSetter<C>, C extends Gtk.Widget = Gtk.Widget>(
309
- childType = "GtkWidget",
318
+ childClass: ChildClass<C>,
310
319
  ): ElementBehavior<P> =>
311
- slot<P, C>("children", childType, {
320
+ slot<P, C>("children", childClass, {
312
321
  attach: (parent, child) => {
313
322
  parent.setContent(child);
314
323
  },
@@ -318,7 +327,7 @@ const contentSetterSlot = <P extends ContentSetter<C>, C extends Gtk.Widget = Gt
318
327
  });
319
328
 
320
329
  const boxSlot = <P extends BoxLike>(): ElementBehavior<P> =>
321
- slot<P, Gtk.Widget>("children", "GtkWidget", {
330
+ slot<P, Gtk.Widget>("children", Gtk.Widget, {
322
331
  attach: (box, child, info) => box.insertChildAfter(child, info.sibling as Gtk.Widget | null),
323
332
  detach: (box, child) => {
324
333
  box.remove(child);
@@ -333,7 +342,7 @@ const callMethod = <P extends GObject.Object, A>(parent: P, method: MethodKey<P,
333
342
 
334
343
  const methodSlot = <P extends GObject.Object, C extends GObject.Object>(
335
344
  slotName: string,
336
- childType: string,
345
+ childClass: ChildClass<C> | undefined,
337
346
  add: MethodKey<P, C>,
338
347
  remove?: MethodKey<P, C>,
339
348
  ): ElementBehavior => {
@@ -345,15 +354,15 @@ const methodSlot = <P extends GObject.Object, C extends GObject.Object>(
345
354
  };
346
355
  }
347
356
 
348
- return slot<P, C>(slotName, childType, hooks);
357
+ return slot<P, C>(slotName, childClass, hooks);
349
358
  };
350
359
 
351
360
  const setterSlot = <P extends GObject.Object, C extends GObject.Object>(
352
361
  slotName: string,
353
- childType: string,
362
+ childClass: ChildClass<C> | undefined,
354
363
  setter: MethodKey<P, C | null>,
355
364
  ): ElementBehavior =>
356
- slot<P, C>(slotName, childType, {
365
+ slot<P, C>(slotName, childClass, {
357
366
  attach: (parent, child) => {
358
367
  callMethod<P, C | null>(parent, setter, child);
359
368
  },
@@ -364,9 +373,9 @@ const setterSlot = <P extends GObject.Object, C extends GObject.Object>(
364
373
 
365
374
  const indexedSlot = <P extends IndexedChildHost<C>, C extends GObject.Object>(
366
375
  slotName: string,
367
- childType: string,
376
+ childClass: ChildClass<C> | undefined,
368
377
  ): ElementBehavior<P> =>
369
- slot<P, C>(slotName, childType, {
378
+ slot<P, C>(slotName, childClass, {
370
379
  attach: (parent, child, info) => {
371
380
  parent.insert(child, info.index);
372
381
  },
@@ -419,7 +428,7 @@ const wrappingIndexedSlot = <W extends Gtk.Widget, P extends IndexedChildHost<Gt
419
428
  Wrapper: new (props: Props) => W,
420
429
  setChild: (wrapper: W, inner: Gtk.Widget) => void,
421
430
  ): ElementBehavior<P> => ({
422
- ...slot<P, Gtk.Widget>("children", "GtkWidget", {
431
+ ...slot<P, Gtk.Widget>("children", Gtk.Widget, {
423
432
  attach: (parent, child, info) =>
424
433
  parent.insert(wrappedRow(Wrapper, setChild, info.context as RowCache, child), info.index),
425
434
  detach: (parent, child, info) => {
@@ -1,5 +1,6 @@
1
1
  import type * as GObject from "@gtkx/gi/gobject";
2
- import { getWrapperClass, TYPE_INVALID, typeFromName, typeIsA } from "@gtkx/runtime";
2
+ import { TYPE_INVALID, typeFromName, typeIsA } from "@gtkx/runtime";
3
+ import { getExactWrapperClass } from "@gtkx/runtime/internal";
3
4
  import { getOrInsert, pickBy } from "@gtkx/utils";
4
5
  import type { Props } from "./registry.js";
5
6
  import { type TypeInfo, typeInfoFor } from "./metadata.js";
@@ -57,8 +58,8 @@ const constructInput = (info: TypeInfo, props: Props): Props =>
57
58
  (info.constructOnly.has(name) || info.construct.has(name)),
58
59
  );
59
60
 
60
- const instantiate = (type: bigint, input: Props): GObject.Object => {
61
- const cls = getWrapperClass(type) as WidgetConstructor;
61
+ const instantiate = (typeName: string, type: bigint, input: Props): GObject.Object => {
62
+ const cls = getExactWrapperClass(type, typeName) as WidgetConstructor;
62
63
 
63
64
  return new cls(input);
64
65
  };
@@ -66,7 +67,7 @@ const instantiate = (type: bigint, input: Props): GObject.Object => {
66
67
  const createObject = (typeName: string, type: bigint, input: Props): GObject.Object => {
67
68
  const create = ELEMENTS[typeName]?.behaviors?.find((behavior) => behavior.create !== undefined)?.create;
68
69
 
69
- return create === undefined ? instantiate(type, input) : create(input);
70
+ return create === undefined ? instantiate(typeName, type, input) : create(input);
70
71
  };
71
72
 
72
73
  const resolveElementNode = (typeName: string, props: Props, dispatch: Dispatch): ElementNode | LazyNode => {
@@ -9,6 +9,11 @@ import {
9
9
  typeName,
10
10
  typeParent,
11
11
  } from "@gtkx/runtime";
12
+ import {
13
+ elementMetadataVersion,
14
+ registeredElementProperties,
15
+ registeredElementSignals,
16
+ } from "@gtkx/runtime/internal";
12
17
  import { getOrInsert } from "@gtkx/utils";
13
18
  import { properties, type PropertyEntry, signals, userEventSignals } from "virtual:gtkx-config";
14
19
  import { deferredProps, type ElementBehavior, ELEMENTS } from "./registry.js";
@@ -36,6 +41,19 @@ const FLAGS = 1;
36
41
  const DEFAULT_VALUE = 2;
37
42
  const ancestryCache: Map<string, string[]> = new Map();
38
43
  const typeInfoCache: Map<string, TypeInfo> = new Map();
44
+ const metadataCacheState = { seenVersion: elementMetadataVersion() };
45
+
46
+ const dropStaleCaches = (): void => {
47
+ const current = elementMetadataVersion();
48
+
49
+ if (metadataCacheState.seenVersion === current) {
50
+ return;
51
+ }
52
+
53
+ metadataCacheState.seenVersion = current;
54
+ ancestryCache.clear();
55
+ typeInfoCache.clear();
56
+ };
39
57
 
40
58
  const addAncestor = (names: string[], seen: Set<string>, name: string | null): void => {
41
59
  if (name === null || seen.has(name)) {
@@ -75,7 +93,7 @@ const addAll = <T>(target: Set<T>, source: Iterable<T> | undefined): void => {
75
93
  };
76
94
 
77
95
  const accumulateAncestor = (info: TypeInfo, ancestor: string): void => {
78
- Object.assign(info.signals, signals[ancestor] ?? {});
96
+ Object.assign(info.signals, registeredElementSignals[ancestor] ?? signals[ancestor] ?? {});
79
97
  addAll(info.userEventSignals, userEventSignals[ancestor]);
80
98
  info.behaviors.push(...(ELEMENTS[ancestor]?.behaviors ?? []));
81
99
  };
@@ -141,7 +159,7 @@ const buildTypeInfo = (name: string): TypeInfo => {
141
159
  info.isLazy = chain.some((ancestor) => ELEMENTS[ancestor]?.isLazy === true);
142
160
 
143
161
  for (const ancestor of chain.toReversed()) {
144
- Object.assign(info.properties, properties[ancestor] ?? {});
162
+ Object.assign(info.properties, registeredElementProperties[ancestor] ?? properties[ancestor] ?? {});
145
163
  }
146
164
 
147
165
  resolveProperties(info);
@@ -149,7 +167,11 @@ const buildTypeInfo = (name: string): TypeInfo => {
149
167
  return info;
150
168
  };
151
169
 
152
- const typeInfoFor = (name: string): TypeInfo => getOrInsert(typeInfoCache, name, buildTypeInfo);
170
+ const typeInfoFor = (name: string): TypeInfo => {
171
+ dropStaleCaches();
172
+
173
+ return getOrInsert(typeInfoCache, name, buildTypeInfo);
174
+ };
153
175
 
154
176
  const getTypeInfo = (object: GObject.Object): TypeInfo | undefined => {
155
177
  const name = typeName(getClassType(object.constructor as AnyClass));