@gtkx/runtime 1.0.0-rc.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -9
- package/dist/application-class.d.ts +32 -0
- package/dist/application-class.d.ts.map +1 -0
- package/dist/application-class.js +68 -0
- package/dist/application-class.js.map +1 -0
- package/dist/arg.d.ts +9 -3
- package/dist/arg.d.ts.map +1 -1
- package/dist/arg.js +4 -3
- package/dist/arg.js.map +1 -1
- package/dist/bind.d.ts +11 -0
- package/dist/bind.d.ts.map +1 -1
- package/dist/bind.js +11 -0
- package/dist/bind.js.map +1 -1
- package/dist/callback.d.ts +2 -1
- package/dist/callback.d.ts.map +1 -1
- package/dist/callback.js +119 -21
- package/dist/callback.js.map +1 -1
- package/dist/closure.d.ts +26 -0
- package/dist/closure.d.ts.map +1 -0
- package/dist/closure.js +99 -0
- package/dist/closure.js.map +1 -0
- package/dist/descriptors.d.ts +105 -6
- package/dist/descriptors.d.ts.map +1 -1
- package/dist/descriptors.js +56 -12
- package/dist/descriptors.js.map +1 -1
- package/dist/error.d.ts +7 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +1 -1
- package/dist/error.js.map +1 -1
- package/dist/fn.d.ts +24 -3
- package/dist/fn.d.ts.map +1 -1
- package/dist/fn.js +70 -33
- package/dist/fn.js.map +1 -1
- package/dist/index.d.ts +29 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -4
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +1 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +1 -0
- package/dist/internal.js.map +1 -1
- package/dist/library.d.ts +2 -1
- package/dist/library.d.ts.map +1 -1
- package/dist/library.js +2 -1
- package/dist/library.js.map +1 -1
- package/dist/lifecycle.d.ts +52 -10
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +62 -18
- package/dist/lifecycle.js.map +1 -1
- package/dist/listeners.d.ts +6 -0
- package/dist/listeners.d.ts.map +1 -1
- package/dist/listeners.js +3 -1
- package/dist/listeners.js.map +1 -1
- package/dist/mixin.d.ts +16 -2
- package/dist/mixin.d.ts.map +1 -1
- package/dist/mixin.js +18 -6
- package/dist/mixin.js.map +1 -1
- package/dist/native-value.d.ts +8 -0
- package/dist/native-value.d.ts.map +1 -1
- package/dist/native-value.js +12 -3
- package/dist/native-value.js.map +1 -1
- package/dist/object.d.ts +34 -6
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +89 -25
- package/dist/object.js.map +1 -1
- package/dist/param-spec.d.ts +11 -0
- package/dist/param-spec.d.ts.map +1 -0
- package/dist/param-spec.js +149 -0
- package/dist/param-spec.js.map +1 -0
- package/dist/promisify.d.ts +3 -5
- package/dist/promisify.d.ts.map +1 -1
- package/dist/promisify.js +4 -3
- package/dist/promisify.js.map +1 -1
- package/dist/properties.d.ts +39 -7
- package/dist/properties.d.ts.map +1 -1
- package/dist/properties.js +305 -42
- package/dist/properties.js.map +1 -1
- package/dist/register-class.d.ts +119 -14
- package/dist/register-class.d.ts.map +1 -1
- package/dist/register-class.js +109 -80
- package/dist/register-class.js.map +1 -1
- package/dist/registry.d.ts +81 -17
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +72 -26
- package/dist/registry.js.map +1 -1
- package/dist/signal.d.ts +22 -3
- package/dist/signal.d.ts.map +1 -1
- package/dist/signal.js +64 -8
- package/dist/signal.js.map +1 -1
- package/dist/t.d.ts +44 -0
- package/dist/t.d.ts.map +1 -1
- package/dist/t.js.map +1 -1
- package/dist/type.d.ts +2 -2
- package/dist/type.d.ts.map +1 -1
- package/dist/type.js +2 -0
- package/dist/type.js.map +1 -1
- package/dist/value.d.ts +10 -10
- package/dist/value.d.ts.map +1 -1
- package/dist/value.js +44 -22
- package/dist/value.js.map +1 -1
- package/dist/vfunc-call.d.ts +37 -0
- package/dist/vfunc-call.d.ts.map +1 -0
- package/dist/vfunc-call.js +168 -0
- package/dist/vfunc-call.js.map +1 -0
- package/dist/vfunc-seeds.d.ts +12 -0
- package/dist/vfunc-seeds.d.ts.map +1 -0
- package/dist/vfunc-seeds.js +19 -0
- package/dist/vfunc-seeds.js.map +1 -0
- package/dist/vfunc.d.ts +8 -0
- package/dist/vfunc.d.ts.map +1 -0
- package/dist/vfunc.js +23 -0
- package/dist/vfunc.js.map +1 -0
- package/package.json +4 -4
- package/src/application-class.ts +116 -0
- package/src/arg.ts +11 -5
- package/src/bind.ts +11 -0
- package/src/callback.ts +163 -23
- package/src/closure.ts +136 -0
- package/src/descriptors.ts +137 -41
- package/src/error.ts +8 -1
- package/src/fn.ts +104 -39
- package/src/index.ts +36 -7
- package/src/internal.ts +1 -0
- package/src/library.ts +2 -1
- package/src/lifecycle.ts +99 -22
- package/src/listeners.ts +9 -1
- package/src/mixin.ts +27 -8
- package/src/native-value.ts +13 -3
- package/src/object.ts +128 -34
- package/src/param-spec.ts +235 -0
- package/src/promisify.ts +8 -8
- package/src/properties.ts +459 -45
- package/src/register-class.ts +329 -114
- package/src/registry.ts +150 -38
- package/src/signal.ts +101 -17
- package/src/t.ts +44 -0
- package/src/type.ts +3 -2
- package/src/value.ts +57 -19
- package/src/vfunc-call.ts +243 -0
- package/src/vfunc-seeds.ts +36 -0
- package/src/vfunc.ts +31 -0
package/src/index.ts
CHANGED
|
@@ -1,14 +1,35 @@
|
|
|
1
1
|
import "./exit-hook.js";
|
|
2
2
|
|
|
3
|
+
/** @public */
|
|
4
|
+
export { type ApplicationClass, type CommandLineApplication, createApplication } from "./application-class.js";
|
|
5
|
+
/** @public */
|
|
6
|
+
export { type ClosureCallback, ClosureMarshalError, toClosure, tryToClosure } from "./closure.js";
|
|
7
|
+
/** @public */
|
|
3
8
|
export { createErrorDomain, type ErrorDomain } from "./error.js";
|
|
4
|
-
|
|
9
|
+
/** @public */
|
|
10
|
+
export { onExit, quit, quitApplication, runApplication, type RunApplicationResult } from "./lifecycle.js";
|
|
11
|
+
/** @public */
|
|
5
12
|
export { offSignal, onceSignal, onSignal } from "./listeners.js";
|
|
13
|
+
/** @public */
|
|
6
14
|
export { installMixins, type Mixin } from "./mixin.js";
|
|
15
|
+
/** @public */
|
|
7
16
|
export { fromNative, toNative } from "./native-value.js";
|
|
8
|
-
|
|
9
|
-
export { type FinishResult, promisify } from "./promisify.js";
|
|
10
|
-
export { registerClass } from "./register-class.js";
|
|
17
|
+
/** @public */
|
|
11
18
|
export {
|
|
19
|
+
type ConstructBinding,
|
|
20
|
+
type ConstructBindings,
|
|
21
|
+
getObjectProperty,
|
|
22
|
+
newObjectWithProperties,
|
|
23
|
+
registerConstructProperties,
|
|
24
|
+
setObjectProperty,
|
|
25
|
+
} from "./object.js";
|
|
26
|
+
/** @public */
|
|
27
|
+
export { promisify } from "./promisify.js";
|
|
28
|
+
/** @public */
|
|
29
|
+
export { type Interface, registerClass } from "./register-class.js";
|
|
30
|
+
/** @public */
|
|
31
|
+
export {
|
|
32
|
+
getClassType,
|
|
12
33
|
getHandle,
|
|
13
34
|
getInstanceType,
|
|
14
35
|
getWrapperClass,
|
|
@@ -16,11 +37,13 @@ export {
|
|
|
16
37
|
registerWrapperClass,
|
|
17
38
|
type StaticBase,
|
|
18
39
|
setHandle,
|
|
19
|
-
tryGetHandle,
|
|
20
40
|
wrapHandle,
|
|
21
41
|
} from "./registry.js";
|
|
22
|
-
|
|
42
|
+
/** @public */
|
|
43
|
+
export { connectSignal, disconnectSignal, emitSignal, getSignalBaseName, type SignalHandler } from "./signal.js";
|
|
44
|
+
/** @public */
|
|
23
45
|
export { t } from "./t.js";
|
|
46
|
+
/** @public */
|
|
24
47
|
export {
|
|
25
48
|
resolveType,
|
|
26
49
|
TYPE_BOOLEAN,
|
|
@@ -55,5 +78,11 @@ export {
|
|
|
55
78
|
typeParent,
|
|
56
79
|
valueIsA,
|
|
57
80
|
} from "./type.js";
|
|
81
|
+
/** @public */
|
|
58
82
|
export { getBoxedValue, setBoxedValue } from "./value.js";
|
|
59
|
-
|
|
83
|
+
/** @public */
|
|
84
|
+
export { callParent, callVfunc } from "./vfunc-call.js";
|
|
85
|
+
/** @public */
|
|
86
|
+
export { alloc, type ExternalObject, type Handle, read, write } from "@gtkx/native";
|
|
87
|
+
/** @public */
|
|
88
|
+
export { type AnyClass } from "@gtkx/utils";
|
package/src/internal.ts
CHANGED
package/src/library.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type BoxedDescriptor, boxedT, type FundamentalDescriptor, fundamentalT
|
|
|
2
2
|
|
|
3
3
|
const LIB = "libgobject-2.0.so.0,libglib-2.0.so.0";
|
|
4
4
|
const VALUE_SIZE = 24;
|
|
5
|
+
const CLOSURE_SIZE = 32;
|
|
5
6
|
const VALUE_T: BoxedDescriptor = boxedT("GValue", { sharedLibrary: LIB, getTypeFnName: "g_value_get_type" });
|
|
6
7
|
|
|
7
8
|
const PARAM_T: FundamentalDescriptor = fundamentalT(LIB, "g_param_spec_ref", "g_param_spec_unref", {
|
|
@@ -14,4 +15,4 @@ const VARIANT_T: FundamentalDescriptor = fundamentalT(LIB, "g_variant_ref", "g_v
|
|
|
14
15
|
typeName: "GVariant",
|
|
15
16
|
});
|
|
16
17
|
|
|
17
|
-
export { LIB, VALUE_SIZE, VALUE_T, PARAM_T, VARIANT_T };
|
|
18
|
+
export { CLOSURE_SIZE, LIB, VALUE_SIZE, VALUE_T, PARAM_T, VARIANT_T };
|
package/src/lifecycle.ts
CHANGED
|
@@ -1,22 +1,46 @@
|
|
|
1
1
|
import { keepAlive, quit as nativeQuit } from "@gtkx/native";
|
|
2
|
-
import {
|
|
2
|
+
import { isDerivedApplication, type LocalCommandLineApplication, shutDownThroughRun } from "./application-class.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* The GIO and GTK application surface {@link runApplication} and {@link quitApplication} drive, so any
|
|
6
|
+
* application subclass satisfies it structurally; the window members only `Gtk.Application` defines are
|
|
7
|
+
* optional.
|
|
7
8
|
*/
|
|
8
9
|
type ApplicationLike = {
|
|
10
|
+
/** Returns whether the application has already been registered. */
|
|
9
11
|
getIsRegistered(): boolean;
|
|
12
|
+
/** Returns whether another process owns the application ID, making this one a remote instance. */
|
|
13
|
+
getIsRemote?(): boolean;
|
|
14
|
+
/** Registers the application with the session, returning whether it succeeded. */
|
|
10
15
|
register(cancellable: null): boolean;
|
|
16
|
+
/** Emits `activate`, bringing up the application's initial user interface. */
|
|
11
17
|
activate(): void;
|
|
12
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Runs GLib's own `g_application_run`, whose tail emits `shutdown`, destroys the application
|
|
20
|
+
* implementation and clears the registration.
|
|
21
|
+
*/
|
|
13
22
|
run(argv: string[]): number;
|
|
23
|
+
/** Returns the windows currently attached to the application. */
|
|
14
24
|
getWindows?(): object[];
|
|
25
|
+
/** Detaches a window, so it no longer holds the application open. */
|
|
15
26
|
removeWindow?(window: object): void;
|
|
27
|
+
/** Connects a handler to the application's `activate` or `shutdown` signal. */
|
|
16
28
|
on(signal: "activate" | "shutdown", handler: () => void): unknown;
|
|
29
|
+
/** Emits one of the application's own signals. */
|
|
30
|
+
emit(signal: "shutdown"): unknown;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/** What {@link runApplication} reports about the process it just started. */
|
|
34
|
+
type RunApplicationResult = {
|
|
35
|
+
/** Whether this process owns the application ID and may build a user interface. */
|
|
36
|
+
isPrimary: boolean;
|
|
37
|
+
/** The status GLib determined for the command line, which the process should exit with. */
|
|
38
|
+
exitStatus: number;
|
|
17
39
|
};
|
|
18
40
|
|
|
19
41
|
const shutdownCallbacks: (() => void)[] = [];
|
|
42
|
+
const startedApplications: WeakSet<object> = new WeakSet();
|
|
43
|
+
const shutDownApplications: WeakSet<object> = new WeakSet();
|
|
20
44
|
/**
|
|
21
45
|
* Runs every registered exit callback and shuts down the native runtime. Safe to
|
|
22
46
|
* call more than once; only the first call takes effect.
|
|
@@ -51,13 +75,9 @@ const onExit = (callback: () => void): void => {
|
|
|
51
75
|
shutdownCallbacks.push(callback);
|
|
52
76
|
};
|
|
53
77
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
*
|
|
58
|
-
* @param application The application to register and activate.
|
|
59
|
-
*/
|
|
60
|
-
const runApplication = (application: ApplicationLike): void => {
|
|
78
|
+
const startApplication = (application: ApplicationLike & LocalCommandLineApplication, argv: string[]): number => {
|
|
79
|
+
startedApplications.add(application);
|
|
80
|
+
|
|
61
81
|
application.on("activate", () => {
|
|
62
82
|
keepAlive(true);
|
|
63
83
|
});
|
|
@@ -66,21 +86,78 @@ const runApplication = (application: ApplicationLike): void => {
|
|
|
66
86
|
keepAlive(false);
|
|
67
87
|
});
|
|
68
88
|
|
|
69
|
-
|
|
70
|
-
|
|
89
|
+
return application.runLocalCommandLine(argv)[2];
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const restartApplication = (application: ApplicationLike): number => {
|
|
93
|
+
shutDownApplications.delete(application);
|
|
94
|
+
|
|
95
|
+
if (!application.register(null)) {
|
|
96
|
+
return 1;
|
|
71
97
|
}
|
|
72
98
|
|
|
73
99
|
application.activate();
|
|
100
|
+
|
|
101
|
+
return 0;
|
|
74
102
|
};
|
|
75
103
|
|
|
76
104
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
105
|
+
* Hands `argv` to GLib's own command line handling, which parses the application's options, prints
|
|
106
|
+
* `--help`, runs `handle-local-options`, registers the application, and either activates it or
|
|
107
|
+
* forwards the command line to the process that already owns the application ID. The runtime is
|
|
108
|
+
* held alive while the application is active and released on shutdown.
|
|
109
|
+
*
|
|
110
|
+
* `g_application_run()` is not what starts the application, because it would drive its own main loop
|
|
111
|
+
* and freeze Node; only the local command line handling it delegates to runs here. {@link
|
|
112
|
+
* quitApplication} calls it once no window holds the application open, to reach the teardown only it
|
|
113
|
+
* performs.
|
|
114
|
+
*
|
|
115
|
+
* GLib parses a given application's command line at most once, so starting an application that has
|
|
116
|
+
* already run registers and activates it instead of reading `argv` again.
|
|
117
|
+
*
|
|
118
|
+
* When another process already owns the application ID, this one registers as a remote instance: no
|
|
119
|
+
* user interface may be built here, because a remote application has no `GtkApplicationImpl` and
|
|
120
|
+
* attaching a window to it crashes.
|
|
121
|
+
*
|
|
122
|
+
* @param application The application to start.
|
|
123
|
+
* @param argv The command line, whose first entry names the program as `--help` should print it.
|
|
124
|
+
* @returns Whether this process may build a user interface, and the status to exit with.
|
|
125
|
+
*/
|
|
126
|
+
const runApplication = (application: ApplicationLike, argv: string[]): RunApplicationResult => {
|
|
127
|
+
if (!isDerivedApplication(application)) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
"runApplication: this application was not built by GTKX, so its command line cannot be " +
|
|
130
|
+
"parsed and it cannot be shut down safely; render <GtkApplication> or <AdwApplication>, " +
|
|
131
|
+
"or construct it with createApplication from @gtkx/runtime",
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const exitStatus = startedApplications.has(application)
|
|
136
|
+
? restartApplication(application)
|
|
137
|
+
: startApplication(application, argv);
|
|
138
|
+
|
|
139
|
+
const isPrimary = application.getIsRegistered() && application.getIsRemote?.() !== true;
|
|
140
|
+
|
|
141
|
+
if (isPrimary) {
|
|
142
|
+
keepAlive(true);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return { isPrimary, exitStatus };
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Detaches every window from the application and runs GLib's own shutdown, which emits `shutdown`,
|
|
150
|
+
* destroys the application implementation and releases the D-Bus registration. Does nothing for an
|
|
151
|
+
* application that is not registered, so a repeated call is a no-op.
|
|
152
|
+
*
|
|
153
|
+
* GLib's own shutdown is reachable once per application: reaching it marks the application as
|
|
154
|
+
* quitting, which GLib never undoes. An application that has already quit falls back to emitting
|
|
155
|
+
* `shutdown`, which releases the runtime and leaves the registration for GLib to drop at finalize.
|
|
79
156
|
*
|
|
80
157
|
* @param application The application to shut down.
|
|
81
158
|
*/
|
|
82
159
|
const quitApplication = (application: ApplicationLike): void => {
|
|
83
|
-
if (!application.getIsRegistered()) {
|
|
160
|
+
if (!application.getIsRegistered() || shutDownApplications.has(application)) {
|
|
84
161
|
return;
|
|
85
162
|
}
|
|
86
163
|
|
|
@@ -90,12 +167,12 @@ const quitApplication = (application: ApplicationLike): void => {
|
|
|
90
167
|
application.removeWindow?.(window);
|
|
91
168
|
}
|
|
92
169
|
|
|
93
|
-
application
|
|
94
|
-
application.quit();
|
|
95
|
-
});
|
|
170
|
+
shutDownThroughRun(application);
|
|
96
171
|
|
|
97
|
-
|
|
98
|
-
|
|
172
|
+
if (application.getIsRegistered()) {
|
|
173
|
+
shutDownApplications.add(application);
|
|
174
|
+
application.emit("shutdown");
|
|
175
|
+
}
|
|
99
176
|
};
|
|
100
177
|
|
|
101
|
-
export { onExit, quit, runApplication, quitApplication, type ApplicationLike };
|
|
178
|
+
export { onExit, quit, runApplication, quitApplication, type ApplicationLike, type RunApplicationResult };
|
package/src/listeners.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import { isSignalHandlerConnected, type SignalHandler } from "./signal.js";
|
|
1
|
+
import { isSignalHandlerConnected, type SignalHandler, untrackConnection } from "./signal.js";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* The connect and disconnect surface {@link onSignal}, {@link onceSignal} and {@link offSignal}
|
|
5
|
+
* drive on an emitter.
|
|
6
|
+
*/
|
|
3
7
|
type SignalConnectable = {
|
|
8
|
+
/** Connects a handler to a signal and returns its handler id. */
|
|
4
9
|
connect(signal: string, handler: SignalHandler, isAfter?: boolean): number;
|
|
10
|
+
/** Disconnects the handler previously connected under the given id. */
|
|
5
11
|
disconnect(handlerId: number): void;
|
|
6
12
|
};
|
|
7
13
|
|
|
@@ -75,6 +81,7 @@ function onceSignal(instance: SignalConnectable, signal: string, handler: Signal
|
|
|
75
81
|
|
|
76
82
|
const wrapped: SignalHandler = (...args) => {
|
|
77
83
|
untrackHandlerId(instance, signal, handlerId);
|
|
84
|
+
untrackConnection(instance, handlerId);
|
|
78
85
|
instance.disconnect(handlerId);
|
|
79
86
|
|
|
80
87
|
return handler(...args);
|
|
@@ -101,6 +108,7 @@ function offSignal(instance: SignalConnectable, signal: string, handler: SignalH
|
|
|
101
108
|
}
|
|
102
109
|
|
|
103
110
|
untrackHandlerId(instance, signal, handlerId);
|
|
111
|
+
untrackConnection(instance, handlerId);
|
|
104
112
|
|
|
105
113
|
if (!isSignalHandlerConnected(instance, handlerId)) {
|
|
106
114
|
return;
|
package/src/mixin.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AnyClass, getParentClass } from "@gtkx/utils";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* The signal plumbing every base class passed to a {@link Mixin} provides, so mixed-in interface
|
|
5
|
+
* members can connect and emit without knowing the concrete class.
|
|
6
|
+
*/
|
|
3
7
|
type MixinReceiver = {
|
|
8
|
+
/** Connects a handler to a signal and returns its handler id. */
|
|
4
9
|
connect(signal: string, handler: (...args: unknown[]) => unknown, isAfter?: boolean): number;
|
|
10
|
+
/** Emits a signal with the given arguments and returns whatever the emission produced. */
|
|
5
11
|
emit(signal: string, ...args: unknown[]): unknown;
|
|
6
12
|
};
|
|
7
13
|
|
|
@@ -41,6 +47,12 @@ function copyLayerMember(target: AnyClass, layer: object, key: string): void {
|
|
|
41
47
|
}
|
|
42
48
|
}
|
|
43
49
|
|
|
50
|
+
function copyLayerMembers(target: AnyClass, layer: object): void {
|
|
51
|
+
for (const key of Object.getOwnPropertyNames(layer)) {
|
|
52
|
+
copyLayerMember(target, layer, key);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
44
56
|
/**
|
|
45
57
|
* Copies each mixin's prototype members onto the target class prototype, skipping
|
|
46
58
|
* any member already defined anywhere in the target's class chain.
|
|
@@ -48,12 +60,6 @@ function copyLayerMember(target: AnyClass, layer: object, key: string): void {
|
|
|
48
60
|
* @param target The class whose prototype receives the mixin members.
|
|
49
61
|
* @param mixins The mixins to apply, in order.
|
|
50
62
|
*/
|
|
51
|
-
function copyLayerMembers(target: AnyClass, layer: object): void {
|
|
52
|
-
for (const key of Object.getOwnPropertyNames(layer)) {
|
|
53
|
-
copyLayerMember(target, layer, key);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
63
|
function installMixins(target: AnyClass, mixins: Mixin[]): void {
|
|
58
64
|
const empty: AnyClass<MixinReceiver> = class {
|
|
59
65
|
connect(): number {
|
|
@@ -70,4 +76,17 @@ function installMixins(target: AnyClass, mixins: Mixin[]): void {
|
|
|
70
76
|
}
|
|
71
77
|
}
|
|
72
78
|
|
|
73
|
-
|
|
79
|
+
function dropLayerMembers(layer: AnyClass, names: Set<string>): void {
|
|
80
|
+
for (const name of names) {
|
|
81
|
+
Reflect.deleteProperty(layer.prototype, name);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function insertMixinLayer(target: AnyClass, mixin: Mixin, inheritedNames: Set<string>): void {
|
|
86
|
+
const layer = mixin(getParentClass(target) as AnyClass<MixinReceiver>);
|
|
87
|
+
dropLayerMembers(layer, inheritedNames);
|
|
88
|
+
Object.setPrototypeOf(target.prototype, layer.prototype);
|
|
89
|
+
Object.setPrototypeOf(target, layer);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export { insertMixinLayer, installMixins, type MixinReceiver, type Mixin };
|
package/src/native-value.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Descriptor, ExternalObject, Handle } from "@gtkx/native";
|
|
2
2
|
import type { ArrayDescriptor, FundamentalDescriptor, HashTableDescriptor, StructDescriptor } from "./descriptors.js";
|
|
3
|
-
import {
|
|
3
|
+
import { getHandle, getWrapperClass, wrapHandle, wrapObject } from "./registry.js";
|
|
4
4
|
import { resolveDescriptorType } from "./type.js";
|
|
5
5
|
|
|
6
6
|
type MarshalledKind = "object" | "struct" | "boxed" | "fundamental" | "array" | "hashtable";
|
|
@@ -76,7 +76,7 @@ function fromNative(descriptor: Descriptor, value: unknown): unknown {
|
|
|
76
76
|
|
|
77
77
|
switch (descriptor.kind) {
|
|
78
78
|
case "object": {
|
|
79
|
-
return
|
|
79
|
+
return wrapObject(value);
|
|
80
80
|
}
|
|
81
81
|
case "struct": {
|
|
82
82
|
return wrapHandle(value as ExternalObject<Handle> | null, (descriptor as StructDescriptor).wrapperClass);
|
|
@@ -111,6 +111,14 @@ function hashTableToNative(descriptor: HashTableDescriptor, value: unknown): unk
|
|
|
111
111
|
]);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Converts a JavaScript value into the raw form native code expects, unwrapping
|
|
116
|
+
* object, struct, boxed, and fundamental wrappers back to their handles and
|
|
117
|
+
* recursively converting arrays and maps according to the descriptor.
|
|
118
|
+
*
|
|
119
|
+
* @param descriptor Describes the native type to convert to.
|
|
120
|
+
* @param value The JavaScript value to convert.
|
|
121
|
+
*/
|
|
114
122
|
function toNative(descriptor: Descriptor, value: unknown): unknown {
|
|
115
123
|
if (!isMarshalledDescriptor(descriptor)) {
|
|
116
124
|
return value;
|
|
@@ -121,7 +129,9 @@ function toNative(descriptor: Descriptor, value: unknown): unknown {
|
|
|
121
129
|
case "struct":
|
|
122
130
|
case "boxed":
|
|
123
131
|
case "fundamental": {
|
|
124
|
-
|
|
132
|
+
const instance = value as object | null | undefined;
|
|
133
|
+
|
|
134
|
+
return instance == null ? null : getHandle(instance);
|
|
125
135
|
}
|
|
126
136
|
case "array": {
|
|
127
137
|
return collectionToNative(descriptor, value);
|
package/src/object.ts
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Descriptor, type ExternalObject, type Handle, newObject } from "@gtkx/native";
|
|
2
|
+
import { type AnyClass, getParentClass } from "@gtkx/utils";
|
|
2
3
|
import { bind } from "./bind.js";
|
|
3
|
-
import {
|
|
4
|
-
import { LIB,
|
|
5
|
-
import {
|
|
4
|
+
import { objectT, stringT, voidT } from "./descriptors.js";
|
|
5
|
+
import { LIB, VALUE_T } from "./library.js";
|
|
6
|
+
import { type ConstructProperty, constructPropertyFor } from "./properties.js";
|
|
7
|
+
import { getHandle, registerWrapper } from "./registry.js";
|
|
6
8
|
import { fromValue, newValueForDescriptor, toValue } from "./value.js";
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
);
|
|
10
|
+
/**
|
|
11
|
+
* One construct property a wrapper class accepts: the canonical `GObject` name it is set under,
|
|
12
|
+
* and the descriptor its value is marshalled through.
|
|
13
|
+
*/
|
|
14
|
+
type ConstructBinding = [name: string, descriptor: Descriptor];
|
|
15
|
+
/** The construct properties a wrapper class accepts, keyed by the camelCased name callers give them. */
|
|
16
|
+
type ConstructBindings = Record<string, ConstructBinding>;
|
|
17
|
+
type ResolvedBindings = { generation: number; bindings: ConstructBindings };
|
|
18
|
+
|
|
19
|
+
const declaredBindings: WeakMap<AnyClass, ConstructBindings> = new WeakMap();
|
|
20
|
+
const resolvedBindings: WeakMap<AnyClass, ResolvedBindings> = new WeakMap();
|
|
21
|
+
const declarations = { generation: 0 };
|
|
22
|
+
const NO_BINDINGS: ConstructBindings = Object.create(null) as ConstructBindings;
|
|
19
23
|
|
|
20
24
|
const gObjectGetProperty = bind(
|
|
21
25
|
LIB,
|
|
@@ -31,37 +35,120 @@ const gObjectSetProperty = bind(
|
|
|
31
35
|
voidT,
|
|
32
36
|
);
|
|
33
37
|
|
|
38
|
+
function collectDeclaredBindings(cls: AnyClass): ConstructBindings[] {
|
|
39
|
+
const declared: ConstructBindings[] = [];
|
|
40
|
+
let current: AnyClass | null = cls;
|
|
41
|
+
|
|
42
|
+
while (current !== null) {
|
|
43
|
+
const own = declaredBindings.get(current);
|
|
44
|
+
|
|
45
|
+
if (own !== undefined) {
|
|
46
|
+
declared.push(own);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
current = getParentClass(current);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return declared;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function mergeDeclaredBindings(cls: AnyClass): ConstructBindings {
|
|
56
|
+
const declared = collectDeclaredBindings(cls);
|
|
57
|
+
const merged: ConstructBindings = Object.create(null) as ConstructBindings;
|
|
58
|
+
|
|
59
|
+
for (let index = declared.length - 1; index >= 0; index--) {
|
|
60
|
+
Object.assign(merged, declared[index]);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return merged;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function constructBindingsFor(cls: AnyClass | undefined): ConstructBindings {
|
|
67
|
+
if (cls === undefined) {
|
|
68
|
+
return NO_BINDINGS;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const cached = resolvedBindings.get(cls);
|
|
72
|
+
|
|
73
|
+
if (cached?.generation === declarations.generation) {
|
|
74
|
+
return cached.bindings;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const bindings = mergeDeclaredBindings(cls);
|
|
78
|
+
resolvedBindings.set(cls, { generation: declarations.generation, bindings });
|
|
79
|
+
|
|
80
|
+
return bindings;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Declares the construct properties a wrapper class accepts, so `newObjectWithProperties`
|
|
85
|
+
* marshals each one through its descriptor rather than resolving it from the
|
|
86
|
+
* `GObject.ParamSpec` the type installs. A class inherits the declarations of its ancestors.
|
|
87
|
+
* Registering takes effect immediately, including for classes already constructed from and for
|
|
88
|
+
* subclasses that already inherited an earlier declaration.
|
|
89
|
+
*
|
|
90
|
+
* @param cls The wrapper class the properties are declared on.
|
|
91
|
+
* @param bindings CamelCased property names mapped to their canonical name and descriptor.
|
|
92
|
+
*/
|
|
93
|
+
function registerConstructProperties(cls: AnyClass, bindings: ConstructBindings): void {
|
|
94
|
+
declaredBindings.set(cls, bindings);
|
|
95
|
+
declarations.generation += 1;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function constructPropertyForEntry(
|
|
99
|
+
source: { gtype: bigint; bindings: ConstructBindings; wrapper: object },
|
|
100
|
+
name: string,
|
|
101
|
+
value: unknown,
|
|
102
|
+
): ConstructProperty | undefined {
|
|
103
|
+
if (value === undefined) {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const binding = source.bindings[name];
|
|
108
|
+
|
|
109
|
+
if (binding === undefined) {
|
|
110
|
+
return constructPropertyFor(source.gtype, name, value, source.wrapper);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return { name: binding[0], value: toValue(binding[1], value) };
|
|
114
|
+
}
|
|
115
|
+
|
|
34
116
|
/**
|
|
35
117
|
* Constructs a new GObject of the given type, setting the supplied construct
|
|
36
|
-
* properties
|
|
37
|
-
*
|
|
118
|
+
* properties, and binds `wrapper` to it. A property the wrapper's class declares
|
|
119
|
+
* through `registerConstructProperties` is marshalled through its descriptor; any
|
|
120
|
+
* other one is marshalled through the `GObject.ParamSpec` the type installs under
|
|
121
|
+
* that name, dashed or camelCased, and is skipped when the type installs none.
|
|
122
|
+
* A value that ParamSpec would refuse throws before GObject sees it: a `TypeError`
|
|
123
|
+
* for a read-only property and for a value of a type the property cannot hold, and
|
|
124
|
+
* a `RangeError` for a value the ParamSpec rejects.
|
|
125
|
+
* Properties whose value is `undefined` are skipped. A type registered with
|
|
126
|
+
* `registerClass` binds the wrapper before its `constructed` slot runs, so an
|
|
127
|
+
* override of that slot already sees a usable instance.
|
|
38
128
|
*
|
|
39
129
|
* @param gtype The GType of the object to construct.
|
|
40
|
-
* @param props Property names mapped to
|
|
130
|
+
* @param props Property names mapped to the values to set them to.
|
|
131
|
+
* @param wrapper The wrapper instance to bind to the new object.
|
|
41
132
|
* @returns The handle of the newly created object.
|
|
42
133
|
*/
|
|
43
|
-
function newObjectWithProperties(gtype: bigint, props:
|
|
134
|
+
function newObjectWithProperties(gtype: bigint, props: object, wrapper: object): ExternalObject<Handle> {
|
|
44
135
|
const names: string[] = [];
|
|
45
136
|
const values: ExternalObject<Handle>[] = [];
|
|
137
|
+
const bindings = constructBindingsFor(wrapper.constructor as AnyClass | undefined);
|
|
138
|
+
const source = { gtype, bindings, wrapper };
|
|
46
139
|
|
|
47
|
-
for (const name
|
|
48
|
-
const
|
|
140
|
+
for (const name of Object.keys(props)) {
|
|
141
|
+
const property = constructPropertyForEntry(source, name, Reflect.get(props, name));
|
|
49
142
|
|
|
50
|
-
if (
|
|
51
|
-
|
|
143
|
+
if (property !== undefined) {
|
|
144
|
+
names.push(property.name);
|
|
145
|
+
values.push(property.value);
|
|
52
146
|
}
|
|
53
|
-
|
|
54
|
-
const [descriptor, value] = entry as [Descriptor, unknown];
|
|
55
|
-
|
|
56
|
-
if (value === undefined) {
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
names.push(name);
|
|
61
|
-
values.push(toValue(descriptor, value));
|
|
62
147
|
}
|
|
63
148
|
|
|
64
|
-
|
|
149
|
+
newObject(gtype, names, values, wrapper, registerWrapper);
|
|
150
|
+
|
|
151
|
+
return getHandle(wrapper);
|
|
65
152
|
}
|
|
66
153
|
|
|
67
154
|
/**
|
|
@@ -92,4 +179,11 @@ function setObjectProperty(obj: object, propertyName: string, descriptor: Descri
|
|
|
92
179
|
gObjectSetProperty(getHandle(obj), propertyName, toValue(descriptor, jsValue));
|
|
93
180
|
}
|
|
94
181
|
|
|
95
|
-
export {
|
|
182
|
+
export {
|
|
183
|
+
newObjectWithProperties,
|
|
184
|
+
getObjectProperty,
|
|
185
|
+
registerConstructProperties,
|
|
186
|
+
setObjectProperty,
|
|
187
|
+
type ConstructBinding,
|
|
188
|
+
type ConstructBindings,
|
|
189
|
+
};
|