@gtkx/runtime 2.0.0-beta.1 → 2.0.0-beta.10

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 (74) hide show
  1. package/README.md +43 -40
  2. package/dist/descriptors.d.ts +2 -1
  3. package/dist/descriptors.d.ts.map +1 -1
  4. package/dist/descriptors.js +5 -1
  5. package/dist/descriptors.js.map +1 -1
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/internal.d.ts +17 -0
  11. package/dist/internal.d.ts.map +1 -1
  12. package/dist/internal.js +13 -0
  13. package/dist/internal.js.map +1 -1
  14. package/dist/lifecycle.js +1 -1
  15. package/dist/lifecycle.js.map +1 -1
  16. package/dist/listeners.d.ts +3 -13
  17. package/dist/listeners.d.ts.map +1 -1
  18. package/dist/listeners.js +12 -12
  19. package/dist/listeners.js.map +1 -1
  20. package/dist/mixin.d.ts +5 -3
  21. package/dist/mixin.d.ts.map +1 -1
  22. package/dist/mixin.js +36 -9
  23. package/dist/mixin.js.map +1 -1
  24. package/dist/object.d.ts +14 -3
  25. package/dist/object.d.ts.map +1 -1
  26. package/dist/object.js +20 -27
  27. package/dist/object.js.map +1 -1
  28. package/dist/properties.d.ts +4 -2
  29. package/dist/properties.d.ts.map +1 -1
  30. package/dist/properties.js +69 -16
  31. package/dist/properties.js.map +1 -1
  32. package/dist/property-brand.d.ts +5 -0
  33. package/dist/property-brand.d.ts.map +1 -0
  34. package/dist/property-brand.js +5 -0
  35. package/dist/property-brand.js.map +1 -0
  36. package/dist/register-class.d.ts +102 -20
  37. package/dist/register-class.d.ts.map +1 -1
  38. package/dist/register-class.js +19 -37
  39. package/dist/register-class.js.map +1 -1
  40. package/dist/registry.d.ts +1 -1
  41. package/dist/registry.d.ts.map +1 -1
  42. package/dist/registry.js +4 -4
  43. package/dist/registry.js.map +1 -1
  44. package/dist/signal-brand.d.ts +12 -0
  45. package/dist/signal-brand.d.ts.map +1 -0
  46. package/dist/signal-brand.js +6 -0
  47. package/dist/signal-brand.js.map +1 -0
  48. package/dist/signal.d.ts +39 -2
  49. package/dist/signal.d.ts.map +1 -1
  50. package/dist/signal.js +75 -5
  51. package/dist/signal.js.map +1 -1
  52. package/dist/type.d.ts.map +1 -1
  53. package/dist/type.js +1 -1
  54. package/dist/type.js.map +1 -1
  55. package/dist/value.d.ts +2 -1
  56. package/dist/value.d.ts.map +1 -1
  57. package/dist/value.js +28 -7
  58. package/dist/value.js.map +1 -1
  59. package/package.json +7 -5
  60. package/src/descriptors.ts +7 -0
  61. package/src/index.ts +2 -2
  62. package/src/internal.ts +42 -0
  63. package/src/lifecycle.ts +1 -1
  64. package/src/listeners.ts +21 -26
  65. package/src/mixin.ts +55 -9
  66. package/src/object.ts +57 -7
  67. package/src/properties.ts +94 -17
  68. package/src/property-brand.ts +5 -0
  69. package/src/register-class.ts +296 -95
  70. package/src/registry.ts +4 -4
  71. package/src/signal-brand.ts +29 -0
  72. package/src/signal.ts +177 -6
  73. package/src/type.ts +1 -1
  74. package/src/value.ts +40 -5
@@ -19,6 +19,11 @@ import {
19
19
  SET_PROPERTY_VFUNC,
20
20
  toNativeProperties,
21
21
  } from "./properties.js";
22
+ import {
23
+ descriptorFreePropertySpec,
24
+ propertyMapOverride,
25
+ writablePropertyMapOverride,
26
+ } from "./property-brand.js";
22
27
  import {
23
28
  getClassStructClass,
24
29
  getClassType,
@@ -30,13 +35,19 @@ import {
30
35
  wrapHandle,
31
36
  } from "./registry.js";
32
37
  import {
38
+ classSignalMember,
39
+ naturalSignalMember,
40
+ signalEmitMapOverride,
41
+ signalMapOverride,
42
+ } from "./signal-brand.js";
43
+ import {
44
+ canonicalSignalName,
33
45
  connectClosureSignal,
34
46
  type DeclaredSignalTypes,
35
47
  emitDeclaredSignal,
36
- getSignalBaseName,
48
+ installSignalDispatch,
37
49
  overrideSignalClassClosure,
38
50
  signalForHandlerName,
39
- type SignalHandler,
40
51
  signalIdFor,
41
52
  } from "./signal.js";
42
53
  import {
@@ -53,9 +64,9 @@ import {
53
64
  import { findClassVfuncDescriptor, findInterfaceVfuncDescriptor } from "./vfunc.js";
54
65
 
55
66
  /** A generated interface value, such as `Gio.ListModel`, accepted by {@link registerClass}. */
56
- type Interface<TImpl> = AnyClass & {
67
+ type Interface<TImpl, TInstance = object> = AnyClass & {
57
68
  /** Type-level slot holding the interface's `Impl` type; no value ever carries it. */
58
- __impl__: (impl: Partial<TImpl> & object) => void;
69
+ __impl__: (impl: Partial<TImpl> & object) => TInstance;
59
70
  };
60
71
 
61
72
  /** Converts underscores in a property name to canonical dashes. */
@@ -79,31 +90,255 @@ type DeclaredSignalBase<TSignals> = (keyof TSignals & string) | Dashed<keyof TSi
79
90
  type DeclaredSignalName<TSignals> = DeclaredSignalBase<TSignals> | `${DeclaredSignalBase<TSignals>}::${string}`;
80
91
 
81
92
  /** Signal methods added for declared names. */
82
- type DeclaredSignalMethods<TSignals> = {
93
+ type DeclaredSignalMethods<TSignals, TBlockedMembers extends PropertyKey> = {
83
94
  /** Type-level map used by signal hooks. */
84
- __signals__?: Record<DeclaredSignalBase<TSignals>, (...args: never[]) => unknown>;
85
- /** Connects a handler. */
86
- connect(
87
- signal: DeclaredSignalName<TSignals>,
88
- handler: (...args: never[]) => unknown,
89
- isAfter?: boolean,
90
- ): number;
91
- /** Emits a signal. */
92
- emit(sigName: DeclaredSignalName<TSignals>, ...args: unknown[]): unknown;
93
- /** Connects a removable handler. */
94
- on(sigName: DeclaredSignalName<TSignals>, callback: (...args: unknown[]) => unknown, isAfter?: boolean): unknown;
95
- /** Connects a one-shot handler. */
96
- once(sigName: DeclaredSignalName<TSignals>, callback: (...args: unknown[]) => unknown, isAfter?: boolean): unknown;
97
- /** Disconnects a handler. */
98
- off(sigName: DeclaredSignalName<TSignals>, callback: (...args: unknown[]) => unknown): unknown;
99
- };
95
+ __signals__?: Record<DeclaredSignalName<TSignals>, (...args: never[]) => unknown>;
96
+ } &
97
+ Pick<
98
+ {
99
+ connect(
100
+ signal: DeclaredSignalName<TSignals>,
101
+ handler: (...args: never[]) => unknown,
102
+ isAfter?: boolean,
103
+ ): number;
104
+ emit(sigName: DeclaredSignalName<TSignals>, ...args: unknown[]): unknown;
105
+ on(sigName: DeclaredSignalName<TSignals>, callback: (...args: never[]) => unknown, isAfter?: boolean): unknown;
106
+ once(
107
+ sigName: DeclaredSignalName<TSignals>,
108
+ callback: (...args: never[]) => unknown,
109
+ isAfter?: boolean,
110
+ ): unknown;
111
+ off(sigName: DeclaredSignalName<TSignals>, callback: (...args: never[]) => unknown): unknown;
112
+ },
113
+ Exclude<"connect" | "emit" | "off" | "on" | "once", TBlockedMembers>
114
+ >;
100
115
 
101
116
  /** A registered instance with declared signal methods and property metadata. */
102
- type RegisteredInstance<TInstance, TProperties, TSignals> = TInstance &
103
- DeclaredSignalMethods<TSignals> & {
104
- /** Type-level map from installed property name to value type; no value ever carries it. */
105
- __properties__: Pick<TInstance, InstalledNames<TProperties> & keyof TInstance>;
106
- };
117
+ type RegisteredInstance<
118
+ TInstance,
119
+ TProperties,
120
+ TSignals,
121
+ TInterfaces extends readonly Interface<TInstance>[],
122
+ TRemainingInterfaces extends readonly Interface<TInstance>[] = TInterfaces,
123
+ TMemberSurface = object,
124
+ TSignalSurface = TInstance extends { [signalMapOverride]?: infer TResolver }
125
+ ? TResolver extends () => infer TMap
126
+ ? NonNullable<TMap>
127
+ : TInstance extends { __signals__?: infer TMap }
128
+ ? NonNullable<TMap>
129
+ : object
130
+ : TInstance extends { __signals__?: infer TMap }
131
+ ? NonNullable<TMap>
132
+ : object,
133
+ TSignalEmitSurface = TInstance extends { [signalEmitMapOverride]?: infer TResolver }
134
+ ? TResolver extends () => infer TMap
135
+ ? NonNullable<TMap>
136
+ : TInstance extends { __signalEmit__?: infer TMap }
137
+ ? NonNullable<TMap>
138
+ : object
139
+ : TInstance extends { __signalEmit__?: infer TMap }
140
+ ? NonNullable<TMap>
141
+ : object,
142
+ TPropertySurface = TInstance extends { [propertyMapOverride]?: infer TResolver }
143
+ ? TResolver extends () => infer TMap
144
+ ? NonNullable<TMap>
145
+ : TInstance extends { __properties__?: infer TMap }
146
+ ? NonNullable<TMap>
147
+ : object
148
+ : TInstance extends { __properties__?: infer TMap }
149
+ ? NonNullable<TMap>
150
+ : object,
151
+ TWritablePropertySurface = TInstance extends { [writablePropertyMapOverride]?: infer TResolver }
152
+ ? TResolver extends () => infer TMap
153
+ ? NonNullable<TMap>
154
+ : TInstance extends { __writableProperties__?: infer TMap }
155
+ ? NonNullable<TMap>
156
+ : object
157
+ : TInstance extends { __writableProperties__?: infer TMap }
158
+ ? NonNullable<TMap>
159
+ : object,
160
+ TNaturalInstanceMembers extends PropertyKey = TInstance extends {
161
+ [naturalSignalMember]?: infer TMembers;
162
+ }
163
+ ? keyof NonNullable<TMembers>
164
+ : never,
165
+ TClassInstanceSignals extends PropertyKey = TInstance extends {
166
+ [classSignalMember]?: infer TMembers;
167
+ }
168
+ ? keyof NonNullable<TMembers>
169
+ : never,
170
+ TLegacySignalMembers extends PropertyKey = "connect" | "disconnect" | "emit" | "off" | "on" | "once",
171
+ TInstalledPropertyCandidate = TInstance & TMemberSurface,
172
+ TRegisteredPropertySurface = Omit<TPropertySurface, InstalledNames<TProperties>> &
173
+ Pick<
174
+ TInstalledPropertyCandidate,
175
+ (string extends keyof TProperties
176
+ ? never
177
+ : {
178
+ [K in keyof TProperties & string]: TProperties[K] extends {
179
+ readonly [descriptorFreePropertySpec]: true;
180
+ }
181
+ ? Camelized<Dashed<K>>
182
+ : never;
183
+ }[keyof TProperties & string]) & {
184
+ [K in keyof TInstalledPropertyCandidate]-?: Extract<
185
+ TInstalledPropertyCandidate[K],
186
+ (...args: never[]) => unknown
187
+ > extends never
188
+ ? K
189
+ : never;
190
+ }[keyof TInstalledPropertyCandidate]
191
+ >,
192
+ TRegisteredWritablePropertySurface = Omit<TWritablePropertySurface, InstalledNames<TProperties>> &
193
+ Pick<
194
+ TInstalledPropertyCandidate,
195
+ (string extends keyof TProperties
196
+ ? never
197
+ : {
198
+ [K in keyof TProperties & string]: TProperties[K] extends {
199
+ readonly [descriptorFreePropertySpec]: true;
200
+ }
201
+ ? Camelized<Dashed<K>>
202
+ : never;
203
+ }[keyof TProperties & string]) & {
204
+ [K in keyof TInstalledPropertyCandidate]-?: Extract<
205
+ TInstalledPropertyCandidate[K],
206
+ (...args: never[]) => unknown
207
+ > extends never
208
+ ? (<U>(probe: U) => U extends { [P in K]: TInstalledPropertyCandidate[P] } ? 1 : 2) extends
209
+ (<U>(probe: U) =>
210
+ U extends { -readonly [P in K]: TInstalledPropertyCandidate[P] } ? 1 : 2)
211
+ ? K
212
+ : never
213
+ : never;
214
+ }[keyof TInstalledPropertyCandidate]
215
+ >,
216
+ > = TRemainingInterfaces extends readonly [
217
+ infer TInterface extends Interface<TInstance>,
218
+ ...infer TRest extends readonly Interface<TInstance>[],
219
+ ]
220
+ ? (ReturnType<TInterface["__impl__"]> extends infer TInterfaceInstance extends object
221
+ ? RegisteredInstance<
222
+ TInstance,
223
+ TProperties,
224
+ TSignals,
225
+ TInterfaces,
226
+ TRest,
227
+ TMemberSurface &
228
+ Omit<
229
+ TInterfaceInstance,
230
+ | keyof TInstance |
231
+ keyof TMemberSurface |
232
+ "__properties__" |
233
+ "__writableProperties__" |
234
+ "__signalEmit__" |
235
+ "__signals__" |
236
+ TLegacySignalMembers
237
+ > & {
238
+ [K in Extract<
239
+ TInterfaceInstance extends { [naturalSignalMember]?: infer TMembers }
240
+ ? keyof NonNullable<TMembers>
241
+ : never,
242
+ TLegacySignalMembers
243
+ > as K extends TNaturalInstanceMembers | keyof TMemberSurface
244
+ ? never
245
+ : K]: K extends keyof TInterfaceInstance ? TInterfaceInstance[K] : never;
246
+ },
247
+ TInterfaceInstance extends { __signals__?: infer TNextSignalMap } ? Omit<
248
+ TSignalSurface,
249
+ keyof {
250
+ [K in keyof NonNullable<TNextSignalMap> as K extends string
251
+ ? Dashed<
252
+ K extends `${infer TBase}::${string}` ? TBase : K
253
+ > extends TClassInstanceSignals | Dashed<keyof TSignals & string>
254
+ ? never
255
+ : K
256
+ : K]: NonNullable<TNextSignalMap>[K];
257
+ }
258
+ > & {
259
+ [K in keyof NonNullable<TNextSignalMap> as K extends string
260
+ ? Dashed<
261
+ K extends `${infer TBase}::${string}` ? TBase : K
262
+ > extends TClassInstanceSignals | Dashed<keyof TSignals & string>
263
+ ? never
264
+ : K
265
+ : K]: NonNullable<TNextSignalMap>[K];
266
+ }
267
+ : TSignalSurface,
268
+ TInterfaceInstance extends { __signalEmit__?: infer TNextSignalEmitMap } ? Omit<
269
+ TSignalEmitSurface,
270
+ keyof {
271
+ [K in keyof NonNullable<TNextSignalEmitMap> as K extends string
272
+ ? Dashed<
273
+ K extends `${infer TBase}::${string}` ? TBase : K
274
+ > extends TClassInstanceSignals | Dashed<keyof TSignals & string>
275
+ ? never
276
+ : K
277
+ : K]: NonNullable<TNextSignalEmitMap>[K];
278
+ }
279
+ > & {
280
+ [K in keyof NonNullable<TNextSignalEmitMap> as K extends string
281
+ ? Dashed<
282
+ K extends `${infer TBase}::${string}` ? TBase : K
283
+ > extends TClassInstanceSignals | Dashed<keyof TSignals & string>
284
+ ? never
285
+ : K
286
+ : K]: NonNullable<TNextSignalEmitMap>[K];
287
+ }
288
+ : TSignalEmitSurface,
289
+ TPropertySurface &
290
+ Omit<
291
+ TInterfaceInstance extends { [propertyMapOverride]?: infer TResolver }
292
+ ? TResolver extends () => infer TMap
293
+ ? NonNullable<TMap>
294
+ : TInterfaceInstance extends { __properties__?: infer TMap }
295
+ ? NonNullable<TMap>
296
+ : object
297
+ : TInterfaceInstance extends { __properties__?: infer TMap }
298
+ ? NonNullable<TMap>
299
+ : object,
300
+ keyof TPropertySurface
301
+ >,
302
+ TWritablePropertySurface &
303
+ Omit<
304
+ TInterfaceInstance extends { [writablePropertyMapOverride]?: infer TResolver }
305
+ ? TResolver extends () => infer TMap
306
+ ? NonNullable<TMap>
307
+ : TInterfaceInstance extends { __writableProperties__?: infer TMap }
308
+ ? NonNullable<TMap>
309
+ : object
310
+ : TInterfaceInstance extends { __writableProperties__?: infer TMap }
311
+ ? NonNullable<TMap>
312
+ : object,
313
+ keyof TWritablePropertySurface
314
+ >,
315
+ TNaturalInstanceMembers,
316
+ TClassInstanceSignals,
317
+ TLegacySignalMembers
318
+ >
319
+ : never) : TInstance &
320
+ TMemberSurface &
321
+ DeclaredSignalMethods<
322
+ TSignals,
323
+ TNaturalInstanceMembers | Extract<keyof TMemberSurface, TLegacySignalMembers>
324
+ > & {
325
+ [naturalSignalMember]?: Record<
326
+ TNaturalInstanceMembers | Extract<keyof TMemberSurface, TLegacySignalMembers>,
327
+ true
328
+ >;
329
+ [classSignalMember]?: Record<
330
+ TClassInstanceSignals | Dashed<keyof TSignals & string>,
331
+ true
332
+ >;
333
+ [signalMapOverride]?: () => Omit<TSignalSurface, DeclaredSignalName<TSignals>> &
334
+ Record<DeclaredSignalName<TSignals>, (...args: never[]) => unknown>;
335
+ [signalEmitMapOverride]?: () => Omit<TSignalEmitSurface, DeclaredSignalName<TSignals>> &
336
+ Record<DeclaredSignalName<TSignals>, { args: unknown[]; result: unknown }>;
337
+ [propertyMapOverride]?: () => TRegisteredPropertySurface;
338
+ [writablePropertyMapOverride]?: () => TRegisteredWritablePropertySurface;
339
+ __properties__: TRegisteredPropertySurface;
340
+ __writableProperties__: TRegisteredWritablePropertySurface;
341
+ };
107
342
 
108
343
  /** A registered class's construct signature and prototype. */
109
344
  type RegisteredConstructor<TClass, TArgs extends unknown[], TInstance> = {
@@ -118,9 +353,14 @@ type RegisteredParts<TClass, TArgs extends unknown[], TInstance> = Omit<TClass,
118
353
  RegisteredConstructor<TClass, TArgs, TInstance>;
119
354
 
120
355
  /** The registered class with its declared properties and signals. */
121
- type RegisteredClass<TClass extends AnyClass, TProperties, TSignals> =
356
+ type RegisteredClass<
357
+ TClass extends AnyClass,
358
+ TProperties,
359
+ TSignals,
360
+ TInterfaces extends readonly Interface<TClass["prototype"]>[],
361
+ > =
122
362
  TClass extends abstract new (...args: infer TArgs) => infer TInstance
123
- ? RegisteredParts<TClass, TArgs, RegisteredInstance<TInstance, TProperties, TSignals>>
363
+ ? RegisteredParts<TClass, TArgs, RegisteredInstance<TInstance, TProperties, TSignals, TInterfaces>>
124
364
  : never;
125
365
 
126
366
  /** A numeric GType or a generated or registered wrapper class. */
@@ -143,6 +383,7 @@ type RegisterClassOptions<
143
383
  TInstance extends object,
144
384
  TProperties extends Record<string, PropertySpec>,
145
385
  TSignals extends Record<string, SignalSpec>,
386
+ TInterfaces extends readonly Interface<TInstance>[],
146
387
  > = {
147
388
  /** Registered GType name, defaulting to the class name. */
148
389
  typeName?: string;
@@ -156,7 +397,7 @@ type RegisterClassOptions<
156
397
  */
157
398
  classInit?(typeStruct: object): void;
158
399
  /** Interfaces implemented through matching `vfunc` methods. */
159
- implements?: Interface<TInstance>[];
400
+ implements?: TInterfaces;
160
401
  /**
161
402
  * ParamSpecs keyed by their JavaScript property names. Keys and ParamSpec names must canonicalize
162
403
  * to the same member. Writes validate flags, types, and ranges and notify only on changes.
@@ -172,7 +413,12 @@ type RegisterClassOptions<
172
413
  };
173
414
 
174
415
  /** {@link RegisterClassOptions} with the widest instance and property types {@link registerClass} accepts. */
175
- type AnyRegisterClassOptions = RegisterClassOptions<object, Record<string, PropertySpec>, Record<string, SignalSpec>>;
416
+ type AnyRegisterClassOptions = RegisterClassOptions<
417
+ object,
418
+ Record<string, PropertySpec>,
419
+ Record<string, SignalSpec>,
420
+ Interface<object>[]
421
+ >;
176
422
  type VfuncFn = NativeRegisterClassVfunc["fn"];
177
423
  type DiscoveredVfunc = VfuncDescriptor & { methodName: string; fn: VfuncFn };
178
424
  type MethodTable = Map<string, VfuncFn>;
@@ -199,11 +445,6 @@ type PropertyVfuncSpec = {
199
445
  type ArgPatch = { isCallerAllocated: true } | { isCallScoped: true };
200
446
  type DeclaredSignals = { native: NativeRegisterClassSignal[]; table: Map<string, DeclaredSignalTypes> };
201
447
 
202
- type SignalMethodHost = {
203
- connect?: (signal: string, handler: SignalHandler, isAfter?: boolean) => number;
204
- emit?: (sigName: string, ...args: unknown[]) => unknown;
205
- };
206
-
207
448
  const INSTANCE_ARG_INDEX = 0;
208
449
  const VALUE_ARG_INDEX = 2;
209
450
  const TEARDOWN_VFUNC_NAMES: Set<string> = new Set(["dispose", "finalize"]);
@@ -240,10 +481,11 @@ function registerClass<
240
481
  T extends AnyClass,
241
482
  TProperties extends Record<string, PropertySpec> = Record<never, PropertySpec>,
242
483
  TSignals extends Record<string, SignalSpec> = Record<never, SignalSpec>,
484
+ const TInterfaces extends readonly Interface<T["prototype"]>[] = readonly Interface<T["prototype"]>[],
243
485
  >(
244
486
  klass: T,
245
- options?: RegisterClassOptions<T["prototype"], TProperties, TSignals>,
246
- ): RegisteredClass<T, TProperties, TSignals>;
487
+ options?: RegisterClassOptions<T["prototype"], TProperties, TSignals, TInterfaces>,
488
+ ): RegisteredClass<T, TProperties, TSignals, TInterfaces>;
247
489
 
248
490
  function registerClass(klass: AnyClass, options: AnyRegisterClassOptions = {}): AnyClass {
249
491
  const parentType = resolveParentType(klass);
@@ -282,7 +524,7 @@ function registerClass(klass: AnyClass, options: AnyRegisterClassOptions = {}):
282
524
  markDerivedClass(klass);
283
525
  installSignalOverrides(newType, methods);
284
526
  applyInterfaceMixins(klass, adoptedTypes, inheritedNames);
285
- installDeclaredSignalMethods(klass, signals.table, members.names);
527
+ installDeclaredSignalMethods(klass, signals.table);
286
528
  invokeClassInit(options, newType);
287
529
 
288
530
  return klass;
@@ -632,8 +874,6 @@ function buildPropertyVfunc(
632
874
  };
633
875
  }
634
876
 
635
- const canonicalSignalName = (name: string): string => name.replaceAll("_", "-");
636
-
637
877
  function assertLowerCaseSignalName(klass: AnyClass, name: string): void {
638
878
  if (!UPPER_CASE_PATTERN.test(name)) {
639
879
  return;
@@ -742,72 +982,33 @@ function resolveDeclaredSignals(klass: AnyClass, signals: Record<string, SignalS
742
982
  return { native, table };
743
983
  }
744
984
 
745
- function inheritedSignalMethod<T>(inherited: T | undefined, signal: string): T {
746
- if (inherited === undefined) {
747
- throw new Error(`Unknown signal '${signal}'`);
748
- }
749
-
750
- return inherited;
751
- }
752
-
753
- function installDeclaredConnect(
754
- proto: SignalMethodHost,
755
- findDeclared: (signal: string) => DeclaredSignalTypes | undefined,
756
- ): void {
757
- const inheritedConnect = proto.connect;
758
-
759
- proto.connect = function connect(
760
- this: object,
761
- signal: string,
762
- handler: SignalHandler,
763
- isAfter?: boolean,
764
- ): number {
765
- if (findDeclared(signal) === undefined) {
766
- return inheritedSignalMethod(inheritedConnect, signal).call(this, signal, handler, isAfter);
767
- }
768
-
769
- return connectClosureSignal(this, signal, handler, isAfter ?? false);
770
- };
771
- }
772
-
773
- function installDeclaredEmit(
774
- proto: SignalMethodHost,
775
- findDeclared: (signal: string) => DeclaredSignalTypes | undefined,
776
- ): void {
777
- const inheritedEmit = proto.emit;
778
-
779
- proto.emit = function emit(this: object, sigName: string, ...args: unknown[]): unknown {
780
- const declared = findDeclared(sigName);
781
-
782
- if (declared === undefined) {
783
- return inheritedSignalMethod(inheritedEmit, sigName).call(this, sigName, ...args);
784
- }
785
-
786
- return emitDeclaredSignal(this, sigName, declared, args);
787
- };
788
- }
789
-
790
985
  function installDeclaredSignalMethods(
791
986
  klass: AnyClass,
792
987
  table: Map<string, DeclaredSignalTypes>,
793
- definedNames: Set<string>,
794
988
  ): void {
795
989
  if (table.size === 0) {
796
990
  return;
797
991
  }
798
992
 
799
- const proto = klass.prototype as SignalMethodHost;
800
-
801
993
  const findDeclared = (signal: string): DeclaredSignalTypes | undefined =>
802
- table.get(canonicalSignalName(getSignalBaseName(signal)));
994
+ table.get(canonicalSignalName(signal));
803
995
 
804
- if (!definedNames.has("connect")) {
805
- installDeclaredConnect(proto, findDeclared);
806
- }
996
+ installSignalDispatch(klass, table.keys().toArray(), {
997
+ connect(instance, signal, handler, isAfter): number {
998
+ findDeclared(signal);
807
999
 
808
- if (!definedNames.has("emit")) {
809
- installDeclaredEmit(proto, findDeclared);
810
- }
1000
+ return connectClosureSignal(instance, signal, handler, isAfter ?? false);
1001
+ },
1002
+ emit(instance, signal, args): unknown {
1003
+ const declared = findDeclared(signal);
1004
+
1005
+ if (declared === undefined) {
1006
+ throw new Error(`Unknown signal '${signal}'`);
1007
+ }
1008
+
1009
+ return emitDeclaredSignal(instance, signal, declared, args);
1010
+ },
1011
+ });
811
1012
  }
812
1013
 
813
1014
  function toNativeInterface(binding: InterfaceVfuncBinding): NativeRegisterClassInterface {
package/src/registry.ts CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  setWrapper,
11
11
  } from "@gtkx/native";
12
12
  import { type AnyClass, walkClassChain } from "@gtkx/utils";
13
- import { copyLayerMembers, installMixins, type Mixin, type MixinReceiver } from "./mixin.js";
13
+ import { copyLayerMembers, createMixinLayer, installMixins, type Mixin, type MixinReceiver } from "./mixin.js";
14
14
  import {
15
15
  TYPE_INVALID,
16
16
  TYPE_OBJECT,
@@ -402,7 +402,7 @@ function registerInterface(cls: AnyClass, type: bigint, mixin: Mixin, layout?: I
402
402
  * @param interfaces Registered interface classes to adopt, in order.
403
403
  * @throws If an entry is not a registered interface.
404
404
  */
405
- function installInterfaces(cls: AnyClass, interfaces: AnyClass[]): void {
405
+ function installInterfaces(cls: AnyClass, interfaces: AnyClass[], inheritedOverrides: string[] = []): void {
406
406
  const mixins = interfaces.map((iface) => {
407
407
  const mixin = getInterfaceMixin(getClassType(iface));
408
408
 
@@ -413,7 +413,7 @@ function installInterfaces(cls: AnyClass, interfaces: AnyClass[]): void {
413
413
  return mixin;
414
414
  });
415
415
 
416
- installMixins(cls, mixins);
416
+ installMixins(cls, mixins, inheritedOverrides);
417
417
  }
418
418
 
419
419
  /**
@@ -535,7 +535,7 @@ function applyInterfaceMixin(cls: AnyClass, type: bigint, baseType: bigint, appl
535
535
 
536
536
  applied.add(type);
537
537
 
538
- return mixin(cls as AnyClass<MixinReceiver>);
538
+ return createMixinLayer(cls as AnyClass<MixinReceiver>, mixin, new Set());
539
539
  }
540
540
 
541
541
  function createComposedClass(base: AnyClass, runtimeType: bigint): AnyClass {
@@ -0,0 +1,29 @@
1
+ const naturalSignalMember: unique symbol = Symbol("gtkx.naturalSignalMember");
2
+ const classSignalMember: unique symbol = Symbol("gtkx.classSignalMember");
3
+ const signalMapOverride: unique symbol = Symbol("gtkx.signalMapOverride");
4
+ const signalEmitMapOverride: unique symbol = Symbol("gtkx.signalEmitMapOverride");
5
+
6
+ type ResolvedSignalMap<T, TFallback> = T extends {
7
+ [signalMapOverride]?: infer TResolver;
8
+ }
9
+ ? TResolver extends () => infer TMap
10
+ ? NonNullable<TMap>
11
+ : TFallback
12
+ : TFallback;
13
+
14
+ type ResolvedSignalEmitMap<T, TFallback> = T extends {
15
+ [signalEmitMapOverride]?: infer TResolver;
16
+ }
17
+ ? TResolver extends () => infer TMap
18
+ ? NonNullable<TMap>
19
+ : TFallback
20
+ : TFallback;
21
+
22
+ export {
23
+ classSignalMember,
24
+ naturalSignalMember,
25
+ signalEmitMapOverride,
26
+ type ResolvedSignalEmitMap,
27
+ type ResolvedSignalMap,
28
+ signalMapOverride,
29
+ };