@gtkx/runtime 1.6.0 → 2.0.0-beta.1

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.
Files changed (88) hide show
  1. package/README.md +5 -5
  2. package/dist/application-class.d.ts +1 -1
  3. package/dist/application-class.d.ts.map +1 -1
  4. package/dist/application-class.js +1 -2
  5. package/dist/application-class.js.map +1 -1
  6. package/dist/arg.d.ts +4 -1
  7. package/dist/arg.d.ts.map +1 -1
  8. package/dist/arg.js.map +1 -1
  9. package/dist/bind.d.ts.map +1 -1
  10. package/dist/bind.js +1 -9
  11. package/dist/bind.js.map +1 -1
  12. package/dist/callback.d.ts +6 -1
  13. package/dist/callback.d.ts.map +1 -1
  14. package/dist/callback.js +33 -21
  15. package/dist/callback.js.map +1 -1
  16. package/dist/closure.d.ts +3 -1
  17. package/dist/closure.d.ts.map +1 -1
  18. package/dist/closure.js +17 -6
  19. package/dist/closure.js.map +1 -1
  20. package/dist/descriptors.d.ts +22 -51
  21. package/dist/descriptors.d.ts.map +1 -1
  22. package/dist/descriptors.js +53 -27
  23. package/dist/descriptors.js.map +1 -1
  24. package/dist/fn.d.ts.map +1 -1
  25. package/dist/fn.js +23 -9
  26. package/dist/fn.js.map +1 -1
  27. package/dist/folded-lengths.d.ts +2 -1
  28. package/dist/folded-lengths.d.ts.map +1 -1
  29. package/dist/folded-lengths.js +17 -7
  30. package/dist/folded-lengths.js.map +1 -1
  31. package/dist/index.d.ts +2 -24
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +2 -24
  34. package/dist/index.js.map +1 -1
  35. package/dist/internal.d.ts +2 -2
  36. package/dist/internal.d.ts.map +1 -1
  37. package/dist/internal.js +2 -2
  38. package/dist/internal.js.map +1 -1
  39. package/dist/native-value.d.ts +9 -1
  40. package/dist/native-value.d.ts.map +1 -1
  41. package/dist/native-value.js +20 -3
  42. package/dist/native-value.js.map +1 -1
  43. package/dist/properties.d.ts +1 -1
  44. package/dist/properties.d.ts.map +1 -1
  45. package/dist/properties.js +2 -11
  46. package/dist/properties.js.map +1 -1
  47. package/dist/register-class.d.ts +45 -249
  48. package/dist/register-class.d.ts.map +1 -1
  49. package/dist/register-class.js.map +1 -1
  50. package/dist/signal.d.ts.map +1 -1
  51. package/dist/signal.js +9 -22
  52. package/dist/signal.js.map +1 -1
  53. package/dist/t.d.ts +5 -21
  54. package/dist/t.d.ts.map +1 -1
  55. package/dist/t.js +1 -5
  56. package/dist/t.js.map +1 -1
  57. package/dist/type.d.ts.map +1 -1
  58. package/dist/type.js +7 -1
  59. package/dist/type.js.map +1 -1
  60. package/dist/value.d.ts.map +1 -1
  61. package/dist/value.js +7 -1
  62. package/dist/value.js.map +1 -1
  63. package/dist/variant.d.ts +2 -6
  64. package/dist/variant.d.ts.map +1 -1
  65. package/dist/variant.js.map +1 -1
  66. package/dist/vfunc-call.d.ts.map +1 -1
  67. package/dist/vfunc-call.js +2 -11
  68. package/dist/vfunc-call.js.map +1 -1
  69. package/package.json +9 -5
  70. package/src/application-class.ts +2 -2
  71. package/src/arg.ts +4 -1
  72. package/src/bind.ts +1 -12
  73. package/src/callback.ts +53 -29
  74. package/src/closure.ts +31 -6
  75. package/src/descriptors.ts +95 -59
  76. package/src/fn.ts +31 -10
  77. package/src/folded-lengths.ts +23 -10
  78. package/src/index.ts +2 -24
  79. package/src/internal.ts +2 -2
  80. package/src/native-value.ts +24 -3
  81. package/src/properties.ts +3 -16
  82. package/src/register-class.ts +45 -249
  83. package/src/signal.ts +9 -34
  84. package/src/t.ts +5 -21
  85. package/src/type.ts +9 -1
  86. package/src/value.ts +9 -1
  87. package/src/variant.ts +2 -6
  88. package/src/vfunc-call.ts +3 -16
@@ -52,105 +52,60 @@ import {
52
52
  } from "./type.js";
53
53
  import { findClassVfuncDescriptor, findInterfaceVfuncDescriptor } from "./vfunc.js";
54
54
 
55
- /**
56
- * A generated interface value, such as `Gio.ListModel`, in the form {@link registerClass} takes it.
57
- * `__impl__` exists only in the type system: it carries the interface's `Impl` type, of which a class
58
- * has to match every member it declares itself, which rejects a class value and a member whose
59
- * signature does not fit the slot it fills. Leaving a slot to the interface is not rejected,
60
- * because `registerClass` does not reject it either: every member of an `Impl` type is optional,
61
- * and the `Partial` holds that open whatever the type declares. The `object` beside it keeps
62
- * TypeScript from also demanding one member in common. An interface that introspection describes
63
- * no vtable for carries `unknown`, since it has no slot a class could fill.
64
- */
55
+ /** A generated interface value, such as `Gio.ListModel`, accepted by {@link registerClass}. */
65
56
  type Interface<TImpl> = AnyClass & {
66
57
  /** Type-level slot holding the interface's `Impl` type; no value ever carries it. */
67
58
  __impl__: (impl: Partial<TImpl> & object) => void;
68
59
  };
69
60
 
70
- /**
71
- * One key of `RegisterClassOptions.properties` with every underscore turned into the dash a canonical
72
- * GObject property name separates its words with.
73
- */
61
+ /** Converts underscores in a property name to canonical dashes. */
74
62
  type Dashed<TName extends string> = TName extends `${infer THead}_${infer TTail}`
75
63
  ? Dashed<`${THead}-${TTail}`>
76
64
  : TName;
77
65
 
78
- /**
79
- * One dashed key in the camelCase spelling the accessors {@link registerClass} installs carry, which is
80
- * the spelling the hooks that address a property by name take.
81
- */
66
+ /** Converts a dashed property name to its JavaScript spelling. */
82
67
  type Camelized<TName extends string> = TName extends `${infer THead}-${infer TTail}`
83
68
  ? `${THead}${Capitalize<Camelized<TTail>>}`
84
69
  : TName;
85
70
 
86
- /**
87
- * The names a registered class carries in its property map: every key of
88
- * `RegisterClassOptions.properties` in camelCase, whichever spelling it was written in.
89
- * A `properties` object given a type of its own rather than left to inference has `string` for its key
90
- * type and names nothing, because a name only known as `string` addresses no member in particular.
91
- */
71
+ /** Property-map keys in the camelCase spelling hooks use. */
92
72
  type InstalledNames<TProperties> = string extends keyof TProperties
93
73
  ? never
94
74
  : Camelized<Dashed<keyof TProperties & string>>;
95
75
 
96
- /**
97
- * The base names the signal surface takes for the signals `RegisterClassOptions.signals` declares:
98
- * each name as written, plus its canonical spelling with underscores turned into dashes, which
99
- * GObject knows the signal by too.
100
- */
76
+ /** Declared signal names in their original and canonical spellings. */
101
77
  type DeclaredSignalBase<TSignals> = (keyof TSignals & string) | Dashed<keyof TSignals & string>;
102
- /**
103
- * The names `connect` and `emit` take for one of the signals `RegisterClassOptions.signals`
104
- * declares: each spelling of each name, plus its detailed form for a signal emitted with a
105
- * `::detail` suffix.
106
- */
78
+ /** Declared signal names, including detailed forms. */
107
79
  type DeclaredSignalName<TSignals> = DeclaredSignalBase<TSignals> | `${DeclaredSignalBase<TSignals>}::${string}`;
108
80
 
109
- /**
110
- * The `connect` and `emit` signatures instances gain for the signals
111
- * `RegisterClassOptions.signals` declares, widening the inherited ones, which take only the names
112
- * introspection knows. A class declaring no signals gains nothing, since no name reaches the
113
- * added signatures.
114
- */
81
+ /** Signal methods added for declared names. */
115
82
  type DeclaredSignalMethods<TSignals> = {
116
- /**
117
- * Type-level map from declared signal name to handler signature, feeding the hooks that
118
- * address a signal by name, such as `useSignal` from `@gtkx/react`; no value ever carries it.
119
- */
83
+ /** Type-level map used by signal hooks. */
120
84
  __signals__?: Record<DeclaredSignalBase<TSignals>, (...args: never[]) => unknown>;
121
- /** Connects a handler to a signal `RegisterClassOptions.signals` declared. */
85
+ /** Connects a handler. */
122
86
  connect(
123
87
  signal: DeclaredSignalName<TSignals>,
124
88
  handler: (...args: never[]) => unknown,
125
89
  isAfter?: boolean,
126
90
  ): number;
127
- /** Emits a signal `RegisterClassOptions.signals` declared. */
91
+ /** Emits a signal. */
128
92
  emit(sigName: DeclaredSignalName<TSignals>, ...args: unknown[]): unknown;
129
- /** Connects a handler to a signal `RegisterClassOptions.signals` declared, for `off` to take off. */
93
+ /** Connects a removable handler. */
130
94
  on(sigName: DeclaredSignalName<TSignals>, callback: (...args: unknown[]) => unknown, isAfter?: boolean): unknown;
131
- /** Connects a handler to a signal `RegisterClassOptions.signals` declared for one emission. */
95
+ /** Connects a one-shot handler. */
132
96
  once(sigName: DeclaredSignalName<TSignals>, callback: (...args: unknown[]) => unknown, isAfter?: boolean): unknown;
133
- /** Disconnects a handler `on` or `once` connected to a signal `RegisterClassOptions.signals` declared. */
97
+ /** Disconnects a handler. */
134
98
  off(sigName: DeclaredSignalName<TSignals>, callback: (...args: unknown[]) => unknown): unknown;
135
99
  };
136
100
 
137
- /**
138
- * An instance of a registered class: everything the class itself declares, plus the property map the
139
- * hooks that address a property by name, such as `useProperty` from `@gtkx/react`, read the installed
140
- * names off. Each name is typed with the value type the class declares for the member of that name,
141
- * so a property the class does not `declare` contributes nothing.
142
- */
101
+ /** A registered instance with declared signal methods and property metadata. */
143
102
  type RegisteredInstance<TInstance, TProperties, TSignals> = TInstance &
144
103
  DeclaredSignalMethods<TSignals> & {
145
104
  /** Type-level map from installed property name to value type; no value ever carries it. */
146
105
  __properties__: Pick<TInstance, InstalledNames<TProperties> & keyof TInstance>;
147
106
  };
148
107
 
149
- /**
150
- * The construct signature and prototype a registered class carries, both giving
151
- * {@link RegisteredInstance}. The signature stays abstract for as long as the class itself is, so
152
- * registering an abstract base leaves it as impossible to construct as it was.
153
- */
108
+ /** A registered class's construct signature and prototype. */
154
109
  type RegisteredConstructor<TClass, TArgs extends unknown[], TInstance> = {
155
110
  /** Object the class's instances inherit from. */
156
111
  prototype: TInstance;
@@ -162,45 +117,24 @@ type RegisteredConstructor<TClass, TArgs extends unknown[], TInstance> = {
162
117
  type RegisteredParts<TClass, TArgs extends unknown[], TInstance> = Omit<TClass, "prototype"> &
163
118
  RegisteredConstructor<TClass, TArgs, TInstance>;
164
119
 
165
- /**
166
- * The class {@link registerClass} hands back: the same class, with the same statics, whose instances
167
- * carry the properties `RegisterClassOptions.properties` installed and take the signals
168
- * `RegisterClassOptions.signals` declares by name. Binding the call to a name, rather than
169
- * discarding it, is what carries those names into the type system.
170
- */
120
+ /** The registered class with its declared properties and signals. */
171
121
  type RegisteredClass<TClass extends AnyClass, TProperties, TSignals> =
172
122
  TClass extends abstract new (...args: infer TArgs) => infer TInstance
173
123
  ? RegisteredParts<TClass, TArgs, RegisteredInstance<TInstance, TProperties, TSignals>>
174
124
  : never;
175
125
 
176
- /**
177
- * A GType in the form `RegisterClassOptions.signals` takes one: the numeric GType itself, such as
178
- * `TYPE_STRING` from `@gtkx/gi/gobject`, or a class carrying one, which is any generated wrapper
179
- * class and any class an earlier {@link registerClass} call registered.
180
- */
126
+ /** A numeric GType or a generated or registered wrapper class. */
181
127
  type SignalGType = bigint | AnyClass<TypedClass>;
182
128
 
183
- /**
184
- * One signal `RegisterClassOptions.signals` creates on the new type, sitting under the signal's
185
- * name. Every part is optional: `{}` declares a signal with no arguments and no return value that
186
- * runs its handlers in the default `RUN_FIRST` stage.
187
- */
129
+ /** A signal installed by {@link registerClass}. */
188
130
  type SignalSpec = {
189
- /**
190
- * `GObject.SignalFlags` bit mask for the signal, defaulting to `RUN_FIRST`. `DETAILED` lets
191
- * handlers connect to and emissions name a `::detail` suffix.
192
- */
131
+ /** `GObject.SignalFlags` bit mask, defaulting to `RUN_FIRST`. */
193
132
  flags?: number;
194
- /** GType of each argument an emission carries, defaulting to none. */
133
+ /** Argument GTypes, defaulting to none. */
195
134
  paramTypes?: SignalGType[];
196
- /** GType of the value an emission returns, defaulting to none. */
135
+ /** Return GType, defaulting to none. */
197
136
  returnType?: SignalGType;
198
- /**
199
- * How the emission combines what its handlers return, limited to the accumulators GObject
200
- * ships: `"first-wins"` stops the emission at the first handler and keeps its result, and
201
- * `"true-handled"` runs handlers until one returns `true`, which requires a boolean
202
- * `returnType`. Without one, every handler runs and the last result stands.
203
- */
137
+ /** Combines handler results; omitted means the last result wins. */
204
138
  accumulator?: "first-wins" | "true-handled";
205
139
  };
206
140
 
@@ -210,129 +144,29 @@ type RegisterClassOptions<
210
144
  TProperties extends Record<string, PropertySpec>,
211
145
  TSignals extends Record<string, SignalSpec>,
212
146
  > = {
213
- /**
214
- * Name to register the new GType under, defaulting to the class's own name. Either way the
215
- * name has to be a valid GType name: at least three characters, starting with a letter or
216
- * underscore, the rest letters, digits, `-`, `_` or `+`. Any other name throws a `TypeError`.
217
- */
147
+ /** Registered GType name, defaulting to the class name. */
218
148
  typeName?: string;
219
- /**
220
- * Registers the new GType abstract, the way `G_TYPE_FLAG_ABSTRACT` marks a C type: the class
221
- * still serves as a parent for further registered subclasses, which instantiate as usual, but
222
- * constructing it directly throws, whether from JavaScript or from a native caller.
223
- */
149
+ /** Prevents direct construction while allowing registered subclasses. */
224
150
  abstract?: boolean;
225
- /**
226
- * Name instances of the new type carry in CSS, applied through `gtk_widget_class_set_css_name`
227
- * from inside the type's `class_init`, so every instance is born with it, wherever it is
228
- * created from. Requires the class to extend `Gtk.Widget`; registering a non-widget with a
229
- * `cssName` throws.
230
- */
151
+ /** CSS node name for a `Gtk.Widget` subclass. */
231
152
  cssName?: string;
232
153
  /**
233
- * Hook run once, synchronously, while `registerClass` registers the type, after its
234
- * `class_init` has installed the vfuncs, properties and signals declared here. It receives the
235
- * new type's class struct wrapped in its generated GTypeStruct wrapper, so class-level setup
236
- * calls such as `Gtk.WidgetClass.installAction`, `Gtk.WidgetClass.addShortcut` and
237
- * `Gtk.WidgetClass.setLayoutManagerType` have somewhere to land. The wrapper serves the
238
- * members of every struct in the parent chain on one object: a widget subclass sees
239
- * `Gtk.WidgetClass` and `GObject.ObjectClass` members alike, so the parameter can be declared
240
- * as whichever of those types the hook needs. The class struct belongs to the type system for
241
- * the life of the process, so keeping the wrapper around past the hook is safe, if rarely
242
- * useful. An exception the hook throws propagates out of `registerClass`, with the type
243
- * already registered: GObject offers no way to unregister a static type.
154
+ * Runs once after registration with the wrapped class struct. An exception propagates after
155
+ * the static GType has already been registered and cannot be undone.
244
156
  */
245
157
  classInit?(typeStruct: object): void;
246
- /**
247
- * Interfaces the new type implements on top of the ones it inherits, given as the interface values
248
- * themselves, such as `Gio.ListModel`. Their vtable slots are filled from the `vfunc`-prefixed methods on
249
- * the class's prototype chain, each of which has to match the interface's `Impl` type, such as
250
- * `Gio.ListModelImpl`.
251
- */
158
+ /** Interfaces implemented through matching `vfunc` methods. */
252
159
  implements?: Interface<TInstance>[];
253
160
  /**
254
- * Properties to install on the new type, keyed by the name JavaScript addresses each one by and
255
- * valued with the `GObject.ParamSpec` describing it. A key is read in camelCase however it is
256
- * written, so `dewPoint`, `dew_point` and `dew-point` all name the same member, and the ParamSpec
257
- * has to carry a name that reads back as that same member, such as `dew-point`, or registration
258
- * throws: the ParamSpec's name is the one GObject emits `notify` with, and a name the key does
259
- * not spell reaches nothing that listens for it. A word starting with a digit is a word of its
260
- * own on either side of that reading, so `level2Depth` takes a ParamSpec named `level-2-depth`
261
- * as readily as one named `level2-depth`, the way `WebKit.Settings` names
262
- * `enable-2d-canvas-acceleration` for its `enable2dCanvasAcceleration` member. An uppercase
263
- * letter in the ParamSpec's own name is refused, since GObject notifies under that spelling
264
- * and nothing else reads it back. Every property gains prototype accessors, one for the key as
265
- * written, one for it with dashes turned into underscores and one for it in camelCase, each
266
- * unless the class already defines that name. They serve the value from storage of their own on
267
- * the instance, which is also what the type's `get_property` and `set_property` slots read and
268
- * write, so a value set from JavaScript, from `g_object_set_property` and at construction all
269
- * land in the same place.
270
- *
271
- * A write the ParamSpec would refuse throws rather than reaching GObject, which reports such a
272
- * write as a GLib critical and drops it: a `TypeError` for a read-only or construct-only
273
- * property and for a value of a type the property cannot hold, and a `RangeError` for a value
274
- * the ParamSpec rejects. The same checks run over a value handed to the constructor, where
275
- * a construct-only property is the one that is writable. An accepted write emits one `notify`,
276
- * which a `freeze_notify` batch collects; a write of the value the property already holds is
277
- * dropped and emits none.
278
- *
279
- * `null` and `undefined` both mean NULL, and mean it only where the ParamSpec's own type holds
280
- * NULL, which is a string, string-array, boxed, object, interface, param, variant or pointer
281
- * property. Such a property holds the `null` either spelling wrote, so the member, the type's
282
- * `get_property` slot and `g_object_get_property` serve the same thing, and writing the other
283
- * spelling over it emits no `notify`. Every other property, so every integer, floating-point,
284
- * boolean, enum, flags and GType one, refuses both with the same `TypeError` it refuses a
285
- * string with, and keeps the value it already holds. It refuses them for what it holds rather
286
- * than for its range, whatever that range is: the type is checked before the range, so a
287
- * `gint` whose range excludes 0 answers a nullish with that same `TypeError` and never with
288
- * the `RangeError` that names the value GObject would put in its place. The one place
289
- * `undefined` means something else is the constructor, which reads it as the property not
290
- * being given at all and leaves it at the ParamSpec's default, so a property is never handed a
291
- * value it cannot serve back.
292
- *
293
- * A floating-point property takes every JavaScript number, `NaN` and both infinities
294
- * included, and its ParamSpec alone rules on which of them the range admits: a `gdouble`
295
- * bounded by `-Infinity` and `Infinity` holds either infinity, and a magnitude a bounded
296
- * one excludes, like any `NaN`, comes back as the `RangeError` that names what GObject
297
- * would put in its place rather than as the `TypeError` a type the property cannot hold
298
- * earns. A `gfloat` property holds what GObject narrows the double to, so it serves `0.1`
299
- * back as `0.10000000149011612` and a finite magnitude no `gfloat` reaches as an infinity,
300
- * which the range then rules on in turn. That narrowing belongs to the property alone: the
301
- * same magnitude written to a `gfloat` through a generated binding, a signal argument or a
302
- * closure return is refused outright rather than narrowed.
303
- *
304
- * A generated property of a wrapped type answers a nullish differently, and the two halves of
305
- * the API disagree here: that property marshals what it is written through its descriptor
306
- * rather than through the checks above, so `new Gtk.Label({ widthRequest: null })` and a later
307
- * write of `null` to that member both land 0, where a `gint` installed here refuses both.
308
- *
309
- * A class that defines the camelCase member itself owns the property: its own accessor decides
310
- * what a write means, the other spellings forward to it, and the type's property slots read
311
- * and write it rather than the generated storage.
161
+ * ParamSpecs keyed by their JavaScript property names. Keys and ParamSpec names must canonicalize
162
+ * to the same member. Writes validate flags, types, and ranges and notify only on changes.
163
+ * Nullish values require a nullable GType; constructor `undefined` means omitted. An existing
164
+ * camelCase accessor remains authoritative.
312
165
  */
313
166
  properties?: TProperties;
314
167
  /**
315
- * Signals to create on the new type, keyed by signal name and valued with the
316
- * {@link SignalSpec} describing each one. A name has to start with a lowercase letter, continue
317
- * in lowercase letters, digits, `-` and `_`, and be new to the type: one an ancestor type or a
318
- * listed interface already carries throws, and so does one carrying an uppercase letter, which
319
- * GObject would carry under that exact spelling, out of reach of both its dashed spelling and
320
- * its `on<SignalName>` default handler. Either word separator spells the same signal, so a
321
- * signal declared as `data_changed` is connected to and emitted as `data-changed` too.
322
- *
323
- * Instances connect and emit by name through the same `connect`, `on`, `once`, `off` and
324
- * `emit` surface inherited signals use: `registerClass` wraps `connect` and `emit` on the
325
- * class's prototype to serve the declared names, unless the class defines the member itself,
326
- * and hands every other name to the inherited implementation. A handler receives the
327
- * emission's arguments without the leading emitter, matching a generated signal, and what it
328
- * returns becomes the emission's return value when the signal declares one.
329
- *
330
- * The declared parameter GTypes rule the emission: `emit` takes exactly one argument per
331
- * declared parameter, throwing a `TypeError` for any other count, and converts each argument
332
- * into a `GValue` of the declared type, throwing for a value that type cannot hold. The signals
333
- * are created with no class closure of their own, but a method named `on<SignalName>`
334
- * becomes the signal's default handler, the way every `on`-prefixed method that names a
335
- * signal the type carries does; see {@link registerClass}.
168
+ * New canonical signal names and specs. Declared GTypes validate emissions, and a matching
169
+ * `on<SignalName>` method becomes the default handler.
336
170
  */
337
171
  signals?: TSignals;
338
172
  };
@@ -388,57 +222,19 @@ const UPPER_CASE_PATTERN = /[A-Z]/;
388
222
  const SIGNAL_OVERRIDE_PATTERN = /^on[A-Z]/;
389
223
 
390
224
  /**
391
- * Registers a subclass of a wrapper class as a new GType, wiring up any class and interface
392
- * virtual functions it overrides, both for the interfaces it inherits and for the ones
393
- * `RegisterClassOptions.implements` names.
394
- *
395
- * Throws when the class does not extend a registered wrapper class, when it has no derivable type
396
- * name or the name is not a valid GType name, when an entry in
397
- * `RegisterClassOptions.implements` is not a registered interface, when a
398
- * listed interface has a prerequisite that neither the parent type nor another listed interface meets,
399
- * when the list names `Gio.AsyncInitable` as an interface the parent type does not already
400
- * implement and no method on the chain fills `vfuncInitAsync`, since the default `init_async`
401
- * would run `vfuncInit` on a worker thread, when an entry in
402
- * `RegisterClassOptions.properties` names its `GObject.ParamSpec` something the key it sits under
403
- * does not spell, when an entry in `RegisterClassOptions.signals` carries an invalid name, a name
404
- * spelled with an uppercase letter rather than dashed, a name the type already knows, a GType
405
- * that cannot hold a value, or an accumulator the spec does not admit, and when
406
- * `RegisterClassOptions.cssName` is
407
- * given for a class that does not extend `Gtk.Widget`. An exception thrown by
408
- * `RegisterClassOptions.classInit` also propagates, after the type has already been registered.
409
- *
410
- * A slot is filled from the `vfunc`-prefixed methods on the class's prototype chain, up to but not
411
- * including the registered ancestor the class extends, so a method an intermediate base class
412
- * declares fills a slot the same way one the class itself declares does. A slot nothing on that
413
- * chain fills is left untouched.
414
- *
415
- * Declare every slot as a method: a class field holding a function, such as `vfuncGetNItems = () => 1`,
416
- * is assigned to each instance after registration and never reaches the vtable.
417
- *
418
- * A method named `on<SignalName>` — the signal's name in camelCase after the `on`, so `onClicked`
419
- * for `clicked` and `onItemsChanged` for `items-changed` — becomes that signal's default handler
420
- * when the type carries the signal, whether an ancestor type or an implemented interface brings it
421
- * or `RegisterClassOptions.signals` declares it. The method is installed as a class-closure
422
- * override, so it runs on every emission, on the instances a native caller creates included, in
423
- * the stage the signal's flags name rather than alongside connected handlers. It receives the
424
- * emission's arguments without the leading emitter, with `this` bound to the emitter, and what it
425
- * returns becomes the emission's result when the signal declares one. The same discovery walks the
426
- * prototype chain vfunc discovery walks, and a subclass registering its own `on<SignalName>`
427
- * replaces the handler for its instances, where `super.on<SignalName>()` reaches the replaced one.
428
- * An `on`-prefixed method naming no signal the type carries is left alone as the ordinary method
429
- * it is.
225
+ * Registers a wrapper subclass as a GType with discovered vfuncs, properties, signals, and interfaces.
226
+ * Vfuncs must be prototype methods; function-valued fields never reach the vtable. A matching
227
+ * `on<SignalName>` method becomes the signal's default handler.
430
228
  *
431
- * An override of `vfuncConstructed` runs from inside the base constructor, before JavaScript
432
- * installs the subclass's field initializers and runs its constructor body, so a field still
433
- * reads `undefined` there and reading a `#private` field throws. Declare state the override
434
- * touches without an initializer, and assign private state from the constructor body after
435
- * `super()`. An instance a native caller creates, through `GObject.newv` or `Gtk.Builder`,
436
- * never runs the subclass constructor at all, so its declared fields stay uninitialized for
437
- * the object's whole life.
229
+ * `vfuncConstructed` runs before field initializers and the constructor body. Native-created
230
+ * instances never run the JavaScript constructor, so native-required state belongs in properties
231
+ * or `vfuncConstructed`. A new `Gio.AsyncInitable` must implement `vfuncInitAsync`.
438
232
  *
439
- * @param klass The subclass to register.
440
- * @param options What the new GType gains beyond the vtable slots the class overrides.
441
- * @returns The same class, now registered, with every name in `options.properties` in its property map.
233
+ * @param klass The wrapper subclass to register.
234
+ * @param options Additional GType configuration.
235
+ * @returns The registered class with declared properties and signals in its type.
236
+ * @throws For invalid parents, names, interfaces, vfuncs, properties, signals, or widget options.
237
+ * A `classInit` exception propagates after the static type has been registered and cannot be undone.
442
238
  */
443
239
  function registerClass<
444
240
  T extends AnyClass,
package/src/signal.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import type { Descriptor, ExternalObject, Handle } from "@gtkx/native";
2
- import { getOrInsert, toCamelIdentifier, upperFirst } from "@gtkx/utils";
2
+ import { toCamelIdentifier, upperFirst } from "@gtkx/utils";
3
3
  import { type Arg, isCallerAllocatedArg, isInoutArg, isOutputArg } from "./arg.js";
4
- import { bind, createBindCache } from "./bind.js";
4
+ import { bind } from "./bind.js";
5
5
  import { wrapCallback } from "./callback.js";
6
- import { toClosure } from "./closure.js";
6
+ import { newCCallbackClosure, toClosure } from "./closure.js";
7
7
  import {
8
8
  arrayT,
9
9
  biguint64T,
@@ -69,7 +69,6 @@ type EmitArg = Arg & {
69
69
  value?: unknown;
70
70
  };
71
71
 
72
- const connectCache = createBindCache();
73
72
  const connectionTable: WeakMap<object, Map<string, Set<number>>> = new WeakMap();
74
73
  const gQuarkFromString = bind(LIB, "g_quark_from_string", [stringT("borrowed")], uint32T);
75
74
  const gSignalLookup = bind(LIB, "g_signal_lookup", [stringT("borrowed"), biguint64T], uint32T);
@@ -129,21 +128,8 @@ const isSignalHandlerConnected = (instance: object, handlerId: number): boolean
129
128
  gSignalHandlerIsConnected(getHandle(instance), handlerId) as boolean;
130
129
 
131
130
  const trackConnection = (instance: object, signal: string, handlerId: number): void => {
132
- let bySignal = connectionTable.get(instance);
133
-
134
- if (!bySignal) {
135
- bySignal = new Map();
136
- connectionTable.set(instance, bySignal);
137
- }
138
-
139
- let handlerIds = bySignal.get(signal);
140
-
141
- if (!handlerIds) {
142
- handlerIds = new Set();
143
- bySignal.set(signal, handlerIds);
144
- }
145
-
146
- handlerIds.add(handlerId);
131
+ const bySignal = connectionTable.getOrInsertComputed(instance, () => new Map<string, Set<number>>());
132
+ bySignal.getOrInsertComputed(signal, () => new Set<number>()).add(handlerId);
147
133
  };
148
134
 
149
135
  const untrackConnection = (instance: object, handlerId: number): void => {
@@ -238,23 +224,11 @@ const buildSignalNames = (type: bigint): string[] => {
238
224
  return [...names];
239
225
  };
240
226
 
241
- const signalNamesFor = (type: bigint): string[] => getOrInsert(signalNameCache, type, buildSignalNames);
227
+ const signalNamesFor = (type: bigint): string[] => signalNameCache.getOrInsertComputed(type, buildSignalNames);
242
228
 
243
229
  const getSignalId = (instance: object, signal: string): number =>
244
230
  signalIdFor((instance as TypedClass).__type__, signal);
245
231
 
246
- function connectBind(type: bigint, signal: string, callback: CallbackDescriptor): (...values: unknown[]) => unknown {
247
- const key = `${String(type)}\0${getSignalBaseName(signal)}`;
248
-
249
- return connectCache(
250
- key,
251
- LIB,
252
- "g_signal_connect_data",
253
- [objectT("borrowed"), stringT("borrowed"), callback, uint32T],
254
- uint64T,
255
- );
256
- }
257
-
258
232
  /**
259
233
  * Connects a handler to a GObject signal on an instance and returns the handler id.
260
234
  * @param instance Emitter to connect to.
@@ -265,8 +239,9 @@ function connectSignal(instance: object, signal: string, spec: SignalConnectSpec
265
239
  const { callback, handler, isAfter } = spec;
266
240
  const wrapped = wrapCallback(handler, callback, "signal");
267
241
  const type: bigint = (instance as TypedClass).__type__;
268
- const connect = connectBind(type, signal, callback);
269
- const handlerId = connect(getHandle(instance), signal, wrapped, isAfter ? 1 : 0) as number;
242
+ const key = `${String(type)}\0${getSignalBaseName(signal)}`;
243
+ const closure = newCCallbackClosure(key, callback, wrapped);
244
+ const handlerId = gSignalConnectClosure(getHandle(instance), signal, closure, isAfter) as number;
270
245
  trackConnection(instance, getSignalBaseName(signal), handlerId);
271
246
 
272
247
  return handlerId;
package/src/t.ts CHANGED
@@ -41,10 +41,7 @@ type T = {
41
41
  unichar: typeof helpers.unicharT;
42
42
  /** Descriptor for an opaque `gpointer` argument, taken from a typed array's memory or a numeric address. */
43
43
  buffer: typeof helpers.bufferT;
44
- /**
45
- * Builds a descriptor for a C string, whose optional length sizes the caller-allocated buffer
46
- * used when the string is passed by reference.
47
- */
44
+ /** Builds a C-string descriptor with optional caller-allocated length. */
48
45
  string: typeof helpers.stringT;
49
46
  /** Builds a descriptor for a `GObject`, wrapped in the class registered for its runtime GType. */
50
47
  object: typeof helpers.objectT;
@@ -82,28 +79,15 @@ type T = {
82
79
  cursorArray: typeof helpers.cursorArrayT;
83
80
  /** Builds a descriptor for a function pointer, marshalling a JavaScript function into a native closure. */
84
81
  callback: typeof helpers.callbackT;
85
- /**
86
- * Binds a native function, wiring up argument directions, `GError` checking, and packing output
87
- * arguments into the result.
88
- */
82
+ /** Binds a native function with argument directions, errors, and packed outputs. */
89
83
  fn: typeof fn;
90
- /**
91
- * Binds a struct field at a fixed offset, compiling its descriptor once into an accessor that
92
- * reads and writes it.
93
- */
84
+ /** Binds a struct field at a fixed offset. */
94
85
  field: typeof field;
95
- /**
96
- * Binds a struct field whose offset is supplied per access, for walking records stored at a
97
- * stride in a buffer.
98
- */
86
+ /** Binds a struct field whose offset is supplied per access. */
99
87
  fieldAt: typeof fieldAt;
100
88
  };
101
89
 
102
- /**
103
- * Type descriptor builder: a collection of factory helpers that describe C types for
104
- * FFI marshalling, plus `bind` to bind a native function and `fn` to describe a
105
- * function signature.
106
- */
90
+ /** FFI descriptor factories and native function binders. */
107
91
  const t: T = {
108
92
  bind,
109
93
  int8: helpers.int8T,
package/src/type.ts CHANGED
@@ -244,6 +244,14 @@ function isResolvableDescriptor(descriptor: Descriptor): descriptor is Resolvabl
244
244
  return RESOLVABLE_DESCRIPTOR_KINDS.has(descriptor.kind);
245
245
  }
246
246
 
247
+ function resolveEnumOrFlagsType(descriptor: Extract<Descriptor, { kind: "enum" | "flags" }>): bigint {
248
+ if (descriptor.getTypeFnName === "") {
249
+ return descriptor.isSigned ? TYPE_INT : TYPE_UINT;
250
+ }
251
+
252
+ return resolveType(descriptor.sharedLibrary, descriptor.getTypeFnName);
253
+ }
254
+
247
255
  function resolveDescriptorType(descriptor: Descriptor): bigint {
248
256
  if (descriptor.kind === "biguint64" && "type" in descriptor) {
249
257
  return TYPE_GTYPE;
@@ -262,7 +270,7 @@ function resolveDescriptorType(descriptor: Descriptor): bigint {
262
270
  switch (descriptor.kind) {
263
271
  case "enum":
264
272
  case "flags": {
265
- return resolveType(descriptor.sharedLibrary, descriptor.getTypeFnName);
273
+ return resolveEnumOrFlagsType(descriptor);
266
274
  }
267
275
  case "boxed": {
268
276
  return resolveBoxedType(descriptor);
package/src/value.ts CHANGED
@@ -422,6 +422,14 @@ const arrayValueType = (descriptor: ArrayDescriptor): ValueType => {
422
422
  throw new Error(`Unsupported array type ${descriptor.arrayKind} of ${descriptor.itemDescriptor.kind}`);
423
423
  };
424
424
 
425
+ const resolveEnumOrFlagsValueType = (descriptor: Extract<Descriptor, { kind: "enum" | "flags" }>): ValueType => {
426
+ if (descriptor.getTypeFnName === "") {
427
+ return descriptor.kind === "flags" ? flagsValueType : enumValueType;
428
+ }
429
+
430
+ return enumOrFlagsValueType(resolveType(descriptor.sharedLibrary, descriptor.getTypeFnName));
431
+ };
432
+
425
433
  const resolveValueType = (descriptor: Descriptor): ValueType => {
426
434
  if (descriptor.kind === "biguint64" && "type" in descriptor) {
427
435
  return typeValueType;
@@ -440,7 +448,7 @@ const resolveValueType = (descriptor: Descriptor): ValueType => {
440
448
  switch (descriptor.kind) {
441
449
  case "enum":
442
450
  case "flags": {
443
- return enumOrFlagsValueType(resolveType(descriptor.sharedLibrary, descriptor.getTypeFnName));
451
+ return resolveEnumOrFlagsValueType(descriptor);
444
452
  }
445
453
  case "boxed": {
446
454
  return boxedValueType(resolveBoxedType(descriptor));
package/src/variant.ts CHANGED
@@ -34,10 +34,7 @@ type BasicValueMap<Nested> = {
34
34
 
35
35
  /** Single-character code of one of the GVariant basic types in {@link BasicValueMap}. */
36
36
  type BasicCode = keyof BasicValueMap<unknown>;
37
- /**
38
- * JavaScript type a byte array (`ay`) unpacks to: the same one the generated bindings use for byte sequences, so a
39
- * `Uint8Array` under the `v2ByteArrays` future flag and a `number[]` otherwise.
40
- */
37
+ /** JavaScript type a byte array (`ay`) unpacks to. */
41
38
  type ByteArray = ReturnType<GLib.Bytes["unrefToArray"]>;
42
39
  /** Values a byte array (`ay`) packs from. */
43
40
  type ByteArrayInput = Uint8Array | number[];
@@ -503,8 +500,7 @@ const toVariant = <S extends string>(typeString: S, value: VariantInput<S>): GLi
503
500
  /**
504
501
  * Unpacks a `GLib.Variant` into the JavaScript value its GVariant type describes, the inverse of
505
502
  * {@link toVariant}. A dictionary keyed by strings unpacks to a record and one keyed by anything
506
- * else to a `Map`, an array to an array, a byte array (`ay`) to the byte array type the generated
507
- * bindings use (`Uint8Array` under the `v2ByteArrays` future flag, `number[]` otherwise), a tuple
503
+ * else to a `Map`, an array to an array, a byte array (`ay`) to a `Uint8Array`, a tuple
508
504
  * to an array of its members, a maybe to its value or `null`, and a nested variant to the
509
505
  * `GLib.Variant` itself unless `recursive` is set, in which case it is unwrapped all the way down.
510
506
  *
package/src/vfunc-call.ts CHANGED
@@ -1,6 +1,5 @@
1
- import type { BindVfuncOptions } from "@gtkx/native";
2
1
  import type { AnyClass } from "@gtkx/utils";
3
- import { bindVfunc } from "@gtkx/native";
2
+ import { bindVfunc, type BindVfuncOptions } from "@gtkx/native";
4
3
  import { type Arg, isCallerAllocatedArg, requiresInputArg } from "./arg.js";
5
4
  import { buildNativeArgTypes, fromNativeCallable } from "./fn.js";
6
5
  import { toNative } from "./native-value.js";
@@ -106,21 +105,9 @@ function buildInvoker(slot: ResolvedSlot, instanceType: bigint | undefined, call
106
105
  }
107
106
 
108
107
  function cachedInvoker(cache: InvokerCache, owner: AnyClass, key: string, build: () => Invoker): Invoker {
109
- let byKey = cache.get(owner);
108
+ const byKey = cache.getOrInsertComputed(owner, () => new Map<string, Invoker>());
110
109
 
111
- if (byKey === undefined) {
112
- byKey = new Map();
113
- cache.set(owner, byKey);
114
- }
115
-
116
- let invoker = byKey.get(key);
117
-
118
- if (invoker === undefined) {
119
- invoker = build();
120
- byKey.set(key, invoker);
121
- }
122
-
123
- return invoker;
110
+ return byKey.getOrInsertComputed(key, build);
124
111
  }
125
112
 
126
113
  function resolveParentSlot(klass: AnyClass, parentType: bigint, methodName: string): ResolvedSlot | undefined {