@decky.fx/raven-tsx 0.1.0 → 0.1.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.
- package/README.md +53 -3
- package/package.json +28 -9
- package/src/core/events.ts +74 -18
- package/src/core/subscriptions.ts +4 -2
- package/src/core/widget-instance.ts +333 -24
- package/src/core/window-instance.ts +423 -8
- package/src/react/host-config.ts +1 -1
- package/src/solid/babel-preset-solid.d.ts +7 -0
- package/src/solid/components.generated.ts +163 -0
- package/src/solid/index.ts +29 -0
- package/src/solid/node.ts +405 -0
- package/src/solid/plugin.ts +108 -0
- package/src/solid/portal.ts +27 -0
- package/src/solid/preload.ts +14 -0
- package/src/solid/primitives.ts +43 -0
- package/src/solid/renderer.ts +37 -0
- package/src/solid/root.ts +137 -0
- package/src/solid/tag.ts +37 -0
- package/src/solid/tags.ts +101 -0
- package/src/solid/teardown.ts +50 -0
- package/src/solid/web.ts +5 -0
|
@@ -75,9 +75,35 @@ function setWindowEvents(window: r.Rid, handlers: ReadonlyMap<string, EventHandl
|
|
|
75
75
|
* it can be anywhere in the tree, and goes away when its element does. It opens once it is placed:
|
|
76
76
|
* a render the framework throws away opens nothing.
|
|
77
77
|
*/
|
|
78
|
+
/** Throws the first of some failures, once all that could run has run. */
|
|
79
|
+
function throwFirst(failures: readonly unknown[]): void {
|
|
80
|
+
if (failures.length > 0) {
|
|
81
|
+
throw failures[0];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Runs a function, and keeps its failure with the others, to go on with what must be done. */
|
|
86
|
+
function attempt(failures: unknown[], run: () => void): void {
|
|
87
|
+
try {
|
|
88
|
+
run();
|
|
89
|
+
} catch (error) {
|
|
90
|
+
failures.push(error);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** A function that runs when a window opens, and what it gave to run when it closes. */
|
|
95
|
+
interface Opening {
|
|
96
|
+
readonly run: () => (() => void) | void;
|
|
97
|
+
cleanup: (() => void) | null;
|
|
98
|
+
}
|
|
99
|
+
|
|
78
100
|
export class WindowInstance implements HostNode {
|
|
79
101
|
readonly tag = "Window";
|
|
80
102
|
readonly nativeWidget = null;
|
|
103
|
+
|
|
104
|
+
get placed(): boolean {
|
|
105
|
+
return this.state === "live" && this.isOpen;
|
|
106
|
+
}
|
|
81
107
|
private id: r.Rid = r.R_INVALID_ID;
|
|
82
108
|
/** Where its widgets go, once it is open. */
|
|
83
109
|
private content: WidgetInstance | null = null;
|
|
@@ -85,7 +111,25 @@ export class WindowInstance implements HostNode {
|
|
|
85
111
|
private readonly initial: HostNode[] = [];
|
|
86
112
|
/** Its `<Island>`: it floats above the content, as in XML, and the window owns it. */
|
|
87
113
|
private island: WidgetInstance | null = null;
|
|
88
|
-
private state: "pending" | "live" | "gone" = "pending";
|
|
114
|
+
private state: "pending" | "live" | "closing" | "gone" = "pending";
|
|
115
|
+
/** A dispose asked for while the window closes: done once it is closed. */
|
|
116
|
+
private disposeAsked = false;
|
|
117
|
+
/** Closed because what holds it left the tree (`closeWindows`): `openWindows` opens it again. */
|
|
118
|
+
private hidden = false;
|
|
119
|
+
/** What runs each time the window opens, with what it returned to run when it closes. */
|
|
120
|
+
private readonly opening = new Set<Opening>();
|
|
121
|
+
/** How many times it opened: a cleanup returned for an opening that is over runs at once. */
|
|
122
|
+
private openings = 0;
|
|
123
|
+
/** While it opens, before its opening pass: a function `whenOpen` gets then runs in that pass, once. */
|
|
124
|
+
private draining = false;
|
|
125
|
+
/** Its handlers were forgotten (`forgetTree`): placed again alive, it gets them back. */
|
|
126
|
+
private forgotten = false;
|
|
127
|
+
/** The widget that holds it in the tree, if any: one at the top of a root, or in a window, has none. */
|
|
128
|
+
private holder: WidgetInstance | null = null;
|
|
129
|
+
/** What the application put in its content itself, kept alive while it is closed. */
|
|
130
|
+
private readonly keptNative: r.Rid[] = [];
|
|
131
|
+
/** The widgets of its content when it closed, in order: what the application put there goes back to its place. */
|
|
132
|
+
private keptOrder: r.Rid[] = [];
|
|
89
133
|
|
|
90
134
|
private constructor(private resolved: ResolvedProps) {}
|
|
91
135
|
|
|
@@ -104,21 +148,249 @@ export class WindowInstance implements HostNode {
|
|
|
104
148
|
if (this.state !== "pending") {
|
|
105
149
|
return;
|
|
106
150
|
}
|
|
151
|
+
// (what holds it was taken out of the tree, alive: it opens once that is placed again, at `openWindows`)
|
|
152
|
+
if (this.holder && !this.holder.attached) {
|
|
153
|
+
this.hidden = true;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
107
156
|
const { window, content } = makeWindow(this.resolved.attributes);
|
|
157
|
+
// (once it is made: one that could not be made still opens with its tree)
|
|
158
|
+
this.hidden = false;
|
|
108
159
|
this.id = window;
|
|
109
160
|
this.content = WidgetInstance.forContainer(content);
|
|
110
161
|
this.state = "live";
|
|
162
|
+
// (user code run by a child may close the window, and open it again: that is another window,
|
|
163
|
+
// whose opening is done; this one is over)
|
|
164
|
+
const current = (): boolean => this.state === "live" && this.id === window;
|
|
165
|
+
this.draining = true;
|
|
111
166
|
try {
|
|
112
167
|
closeEvents.set(window, this.resolved.handlers.get("OnClose"));
|
|
168
|
+
// (with no OnClose, the user closes it at any time: what its openings gave runs then)
|
|
169
|
+
closeEvents.notice(window, () => this.closedByUser());
|
|
113
170
|
setWindowEvents(window, this.resolved.handlers);
|
|
114
|
-
|
|
115
|
-
|
|
171
|
+
// (each child goes in, also past one that fails: then the window goes, with all of them)
|
|
172
|
+
// (one at a time: one that user code released meanwhile is no longer waiting here. And not
|
|
173
|
+
// once user code closed the window: what is left waits for the next opening)
|
|
174
|
+
const failures: unknown[] = [];
|
|
175
|
+
while (current()) {
|
|
176
|
+
const child = this.initial.shift();
|
|
177
|
+
if (!child) {
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
attempt(failures, () => this.append(child));
|
|
181
|
+
}
|
|
182
|
+
throwFirst(failures);
|
|
183
|
+
if (current()) {
|
|
184
|
+
this.restoreNative(content);
|
|
116
185
|
}
|
|
117
186
|
} catch (error) {
|
|
187
|
+
// (only this opening goes: one a child opened again meanwhile, after closing this one, stays)
|
|
188
|
+
if (!(this.state === "live" && this.id !== window)) {
|
|
189
|
+
this.forgetTree();
|
|
190
|
+
this.dispose();
|
|
191
|
+
}
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
// (closed while it opened: that was no opening, and the next placing opens it)
|
|
195
|
+
if (!current()) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
this.draining = false;
|
|
199
|
+
this.openings++;
|
|
200
|
+
this.runOpening();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Puts back what the application put in the content itself, each just before the first widget
|
|
205
|
+
* that came after it when the window closed: its place among the children of the framework.
|
|
206
|
+
*/
|
|
207
|
+
private restoreNative(content: r.Rid): void {
|
|
208
|
+
const order = this.keptOrder;
|
|
209
|
+
this.keptOrder = [];
|
|
210
|
+
for (const widget of this.keptNative.splice(0)) {
|
|
211
|
+
// (one the application placed elsewhere meanwhile is that place's)
|
|
212
|
+
if (!this.stillKept(widget)) {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const at = order.indexOf(widget);
|
|
216
|
+
const after = new Set(at < 0 ? [] : order.slice(at + 1));
|
|
217
|
+
const count = r.Rwidget_get_child_count(content);
|
|
218
|
+
let position = count;
|
|
219
|
+
for (let index = 0; index < count; index++) {
|
|
220
|
+
if (after.has(r.Rwidget_get_child(content, index))) {
|
|
221
|
+
position = index;
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
r.Rwidget_insert_child(content, widget, position);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Destroys what the application put in the content, kept while the window was closed. */
|
|
230
|
+
private destroyKeptNative(): void {
|
|
231
|
+
this.keptOrder = [];
|
|
232
|
+
for (const widget of this.keptNative.splice(0)) {
|
|
233
|
+
if (this.stillKept(widget)) {
|
|
234
|
+
r.Rwidget_destroy(widget);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/** Whether a widget kept while the window is closed is still its own: alive, and in no other parent. */
|
|
240
|
+
private stillKept(widget: r.Rid): boolean {
|
|
241
|
+
return r.Rwidget_is_valid(widget) !== 0 && r.Rwidget_get_parent(widget) === r.R_INVALID_ID;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** Runs what waits for the window to open, each once: the first that threw says so after. */
|
|
245
|
+
private runOpening(): void {
|
|
246
|
+
// (a failure of one is not a failure of the window, which stays. One cancelled meanwhile, or
|
|
247
|
+
// all once the window is gone, disposed or destroyed in Raven, do not run)
|
|
248
|
+
const failures: unknown[] = [];
|
|
249
|
+
const openings = this.openings;
|
|
250
|
+
for (const opening of [...this.opening]) {
|
|
251
|
+
// (a function that closed the window and opened it again started a pass of its own)
|
|
252
|
+
if (this.openings !== openings) {
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
if (!this.opening.has(opening) || !this.isOpen) {
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
this.runOpeningOf(opening);
|
|
260
|
+
} catch (error) {
|
|
261
|
+
failures.push(error);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
throwFirst(failures);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Runs one function of an opening, and keeps what it returns for the closing. If the function
|
|
269
|
+
* cancelled itself or closed the window, that opening is over: what it returned runs now.
|
|
270
|
+
*/
|
|
271
|
+
private runOpeningOf(opening: Opening): void {
|
|
272
|
+
const openings = this.openings;
|
|
273
|
+
const cleanup = opening.run() ?? null;
|
|
274
|
+
if (this.opening.has(opening) && this.state === "live" && this.isOpen && this.openings === openings) {
|
|
275
|
+
opening.cleanup = cleanup;
|
|
276
|
+
} else {
|
|
277
|
+
cleanup?.();
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** Takes the cleanups of the opening that ends, to run once the window is closed. */
|
|
282
|
+
private takeClosing(): (() => void)[] {
|
|
283
|
+
const cleanups: (() => void)[] = [];
|
|
284
|
+
for (const opening of this.opening) {
|
|
285
|
+
if (opening.cleanup) {
|
|
286
|
+
cleanups.push(opening.cleanup);
|
|
287
|
+
opening.cleanup = null;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return cleanups;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Runs a function each time the window opens: now if it is open, and again if it is closed and
|
|
295
|
+
* placed again (`close`). What it returns runs when the window closes. Returns what cancels it,
|
|
296
|
+
* running that too. A framework's ref gives the window before it opens.
|
|
297
|
+
*/
|
|
298
|
+
whenOpen(run: () => (() => void) | void): () => void {
|
|
299
|
+
if (this.state === "gone") {
|
|
300
|
+
return () => {};
|
|
301
|
+
}
|
|
302
|
+
const opening: Opening = { run, cleanup: null };
|
|
303
|
+
this.opening.add(opening);
|
|
304
|
+
// (a window the user closed, or Raven destroyed, does not open again. One that opens runs it with the rest)
|
|
305
|
+
if (this.state === "live" && this.isOpen && !this.draining) {
|
|
306
|
+
try {
|
|
307
|
+
this.runOpeningOf(opening);
|
|
308
|
+
} catch (error) {
|
|
309
|
+
// (the caller gets no way to cancel it: it is not kept. What a run of it gave meanwhile,
|
|
310
|
+
// opening the window again, runs now: nothing would run it after)
|
|
311
|
+
this.opening.delete(opening);
|
|
312
|
+
const cleanup = opening.cleanup;
|
|
313
|
+
opening.cleanup = null;
|
|
314
|
+
try {
|
|
315
|
+
cleanup?.();
|
|
316
|
+
} catch (cleanupError) {
|
|
317
|
+
console.error("raven-tsx: a cleanup of whenOpen failed:", cleanupError);
|
|
318
|
+
}
|
|
319
|
+
throw error;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return () => {
|
|
323
|
+
this.opening.delete(opening);
|
|
324
|
+
const cleanup = opening.cleanup;
|
|
325
|
+
opening.cleanup = null;
|
|
326
|
+
cleanup?.();
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Closes the window and keeps what it holds, alive, to open again when it is placed again: the
|
|
332
|
+
* same instance, as its `ref` gave it. A framework that takes a window out of its tree, and may
|
|
333
|
+
* put it back, closes it. (`window` is then another handle)
|
|
334
|
+
*/
|
|
335
|
+
close(): void {
|
|
336
|
+
if (this.state !== "live") {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (!this.isOpen) {
|
|
340
|
+
// (closed by the user, or destroyed in Raven, with what it held: it does not open again)
|
|
118
341
|
this.forgetTree();
|
|
119
342
|
this.dispose();
|
|
120
|
-
|
|
343
|
+
return;
|
|
121
344
|
}
|
|
345
|
+
// (while it closes, what user code does to it waits: whenOpen for the next opening, dispose for the end)
|
|
346
|
+
this.state = "closing";
|
|
347
|
+
const failures: unknown[] = [];
|
|
348
|
+
const cleanups = this.takeClosing();
|
|
349
|
+
// (what it holds is not shown from now: a window a cleanup adds in it opens at the next opening)
|
|
350
|
+
this.content?.setShown(false);
|
|
351
|
+
this.island?.setShown(false);
|
|
352
|
+
const content = this.content?.widget ?? r.R_INVALID_ID;
|
|
353
|
+
// (the order of the content, before the children of the framework leave it)
|
|
354
|
+
const order = r.Rwidget_is_valid(content) ? Array.from({ length: r.Rwidget_get_child_count(content) }, (_, index) => r.Rwidget_get_child(content, index)) : [];
|
|
355
|
+
// (the windows in it close too, kept to open with it)
|
|
356
|
+
attempt(failures, () => this.content?.closeWindows());
|
|
357
|
+
attempt(failures, () => this.island?.closeWindows());
|
|
358
|
+
const children = this.content?.releaseChildren() ?? [];
|
|
359
|
+
const island = this.island;
|
|
360
|
+
if (island) {
|
|
361
|
+
this.detachIsland(island);
|
|
362
|
+
}
|
|
363
|
+
// (what the application put in the content itself stays alive too. What is still kept from
|
|
364
|
+
// before, not put back since, keeps its place after)
|
|
365
|
+
this.keptOrder = [...order, ...this.keptOrder.filter((widget) => !order.includes(widget))];
|
|
366
|
+
while (r.Rwidget_is_valid(content) && r.Rwidget_get_child_count(content) > 0) {
|
|
367
|
+
const widget = r.Rwidget_get_child(content, 0);
|
|
368
|
+
r.Rwidget_remove_child(content, widget);
|
|
369
|
+
this.keptNative.push(widget);
|
|
370
|
+
}
|
|
371
|
+
closeEvents.set(this.id, undefined, false);
|
|
372
|
+
closeEvents.notice(this.id, undefined, false);
|
|
373
|
+
for (const event of Object.values(WINDOW_EVENTS)) {
|
|
374
|
+
event.off(this.id, false);
|
|
375
|
+
}
|
|
376
|
+
if (this.isOpen) {
|
|
377
|
+
r.Rwindow_destroy(this.id);
|
|
378
|
+
}
|
|
379
|
+
this.id = r.R_INVALID_ID;
|
|
380
|
+
this.content = null;
|
|
381
|
+
this.state = "pending";
|
|
382
|
+
// (before those that still waited: closed while it opened, it had placed these first. The island
|
|
383
|
+
// first: another Island among the children would take its place)
|
|
384
|
+
this.initial.unshift(...(island ? [island] : []), ...children);
|
|
385
|
+
// (last: a cleanup sees the window closed)
|
|
386
|
+
for (const cleanup of cleanups) {
|
|
387
|
+
attempt(failures, cleanup);
|
|
388
|
+
}
|
|
389
|
+
if (this.disposeAsked) {
|
|
390
|
+
this.disposeAsked = false;
|
|
391
|
+
attempt(failures, () => this.dispose());
|
|
392
|
+
}
|
|
393
|
+
throwFirst(failures);
|
|
122
394
|
}
|
|
123
395
|
|
|
124
396
|
/** Whether the window still exists: without an `OnClose`, the user can close it at any time. */
|
|
@@ -126,7 +398,49 @@ export class WindowInstance implements HostNode {
|
|
|
126
398
|
return this.id !== r.R_INVALID_ID && r.Rwindow_is_valid(this.id) !== 0;
|
|
127
399
|
}
|
|
128
400
|
|
|
129
|
-
|
|
401
|
+
/** Gives back what `forgetTree` took: a cleanup placed it elsewhere, alive, as its tree was deleted. */
|
|
402
|
+
rememberTree(): void {
|
|
403
|
+
if (!this.forgotten) {
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
this.forgotten = false;
|
|
407
|
+
if (this.state !== "live" || !this.isOpen) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
this.content?.rememberTree();
|
|
411
|
+
this.island?.rememberTree();
|
|
412
|
+
closeEvents.set(this.id, this.resolved.handlers.get("OnClose"));
|
|
413
|
+
closeEvents.notice(this.id, () => this.closedByUser());
|
|
414
|
+
setWindowEvents(this.id, this.resolved.handlers);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/** The user closed it, with no OnClose to ask: Raven destroys it now. What its openings gave runs. */
|
|
418
|
+
private closedByUser(): void {
|
|
419
|
+
const failures: unknown[] = [];
|
|
420
|
+
for (const cleanup of this.takeClosing()) {
|
|
421
|
+
attempt(failures, cleanup);
|
|
422
|
+
}
|
|
423
|
+
throwFirst(failures);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/** What holds it in the tree, to know whether that is shown: a window opens only then. */
|
|
427
|
+
setParent(parent: WidgetInstance | null): void {
|
|
428
|
+
this.holder = parent;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
closeWindows(): void {
|
|
432
|
+
if (this.state === "live") {
|
|
433
|
+
this.hidden = true;
|
|
434
|
+
this.close();
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
openWindows(): void {
|
|
439
|
+
// (only one closed with its tree: one closed by other code stays closed)
|
|
440
|
+
if (this.hidden) {
|
|
441
|
+
this.materialize();
|
|
442
|
+
}
|
|
443
|
+
}
|
|
130
444
|
|
|
131
445
|
applyParentAttributes(): void {}
|
|
132
446
|
|
|
@@ -174,15 +488,25 @@ export class WindowInstance implements HostNode {
|
|
|
174
488
|
if (child.tag !== "Island" || this.island || !(child instanceof WidgetInstance)) {
|
|
175
489
|
return false;
|
|
176
490
|
}
|
|
491
|
+
// (shown again: closing the window said it was not)
|
|
492
|
+
child.setShown(true);
|
|
177
493
|
child.materialize();
|
|
178
494
|
const set = (widget: r.Rid): void => r.Rwindow_set_island(this.id, widget);
|
|
179
495
|
this.island = child;
|
|
180
496
|
child.setHolder(set);
|
|
181
497
|
set(child.widget);
|
|
498
|
+
// (an island kept while the window was closed opens the windows in it again; one a deleted
|
|
499
|
+
// tree let go, alive, gets back its handlers first)
|
|
500
|
+
child.rememberTree();
|
|
501
|
+
child.openWindows();
|
|
182
502
|
return true;
|
|
183
503
|
}
|
|
184
504
|
|
|
185
505
|
append(child: HostNode): void {
|
|
506
|
+
if (this.state !== "live") {
|
|
507
|
+
this.appendInitial(child);
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
186
510
|
if (child !== this.island && !this.takeIsland(child)) {
|
|
187
511
|
this.open.append(child);
|
|
188
512
|
}
|
|
@@ -192,11 +516,60 @@ export class WindowInstance implements HostNode {
|
|
|
192
516
|
if (this.state === "live") {
|
|
193
517
|
this.append(child);
|
|
194
518
|
} else {
|
|
519
|
+
this.releaseInitial(child);
|
|
195
520
|
this.initial.push(child);
|
|
196
521
|
}
|
|
197
522
|
}
|
|
198
523
|
|
|
524
|
+
/** Takes a child out of those that go in when the window opens. */
|
|
525
|
+
private releaseInitial(child: HostNode): void {
|
|
526
|
+
const index = this.initial.indexOf(child);
|
|
527
|
+
if (index >= 0) {
|
|
528
|
+
this.initial.splice(index, 1);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
release(child: HostNode): void {
|
|
533
|
+
// (also while it opens: what waits to go in is let go too)
|
|
534
|
+
this.releaseInitial(child);
|
|
535
|
+
// (while it closes too, it has its content: what it keeps to open again is read from there)
|
|
536
|
+
if (!this.content) {
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
if (child !== this.island) {
|
|
540
|
+
this.content.release(child);
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
this.detachIsland(child);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/** Takes the island out of the window, alive. */
|
|
547
|
+
private detachIsland(child: HostNode): void {
|
|
548
|
+
this.island = null;
|
|
549
|
+
(child as WidgetInstance).setHolder(null);
|
|
550
|
+
const widget = child.nativeWidget;
|
|
551
|
+
if (this.isOpen && widget !== null && r.Rwidget_is_valid(widget)) {
|
|
552
|
+
// (Rwindow_set_island would delete it: a widget added elsewhere leaves its window alive)
|
|
553
|
+
const holder = r.Rwidget_create_from_xml("<VBox/>");
|
|
554
|
+
r.Rwidget_add_child(holder, widget);
|
|
555
|
+
r.Rwidget_remove_child(holder, widget);
|
|
556
|
+
r.Rwidget_destroy(holder);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
199
560
|
insertBefore(child: HostNode, before: HostNode): void {
|
|
561
|
+
// (while it closes, its children are still in its content, to wait for the next opening in
|
|
562
|
+
// their order: one put before one of them goes there, and waits with them)
|
|
563
|
+
if (this.state === "closing" && this.content && before !== this.island && this.content.has(before)) {
|
|
564
|
+
this.content.insertBefore(child, before);
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
if (this.state !== "live") {
|
|
568
|
+
this.releaseInitial(child);
|
|
569
|
+
const index = this.initial.indexOf(before);
|
|
570
|
+
this.initial.splice(index < 0 ? this.initial.length : index, 0, child);
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
200
573
|
if (child === this.island || this.takeIsland(child)) {
|
|
201
574
|
return;
|
|
202
575
|
}
|
|
@@ -209,8 +582,16 @@ export class WindowInstance implements HostNode {
|
|
|
209
582
|
}
|
|
210
583
|
|
|
211
584
|
remove(child: HostNode): void {
|
|
585
|
+
// (also while it opens: what waits to go in goes from there, and does not go in after)
|
|
586
|
+
this.releaseInitial(child);
|
|
587
|
+
if (!this.content) {
|
|
588
|
+
// (a child can be live already, moved here: its handlers go before it does)
|
|
589
|
+
child.forgetTree();
|
|
590
|
+
child.dispose();
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
212
593
|
if (child !== this.island) {
|
|
213
|
-
this.
|
|
594
|
+
this.content.remove(child);
|
|
214
595
|
return;
|
|
215
596
|
}
|
|
216
597
|
child.forgetTree();
|
|
@@ -228,29 +609,63 @@ export class WindowInstance implements HostNode {
|
|
|
228
609
|
if (this.state !== "live") {
|
|
229
610
|
return;
|
|
230
611
|
}
|
|
612
|
+
this.forgotten = true;
|
|
231
613
|
this.content?.forgetTree();
|
|
232
614
|
this.island?.forgetTree();
|
|
233
615
|
closeEvents.set(this.id, undefined, false);
|
|
616
|
+
closeEvents.notice(this.id, undefined, false);
|
|
234
617
|
for (const event of Object.values(WINDOW_EVENTS)) {
|
|
235
618
|
event.off(this.id, false);
|
|
236
619
|
}
|
|
237
620
|
}
|
|
238
621
|
|
|
239
622
|
dispose(): void {
|
|
623
|
+
if (this.state === "closing") {
|
|
624
|
+
this.disposeAsked = true;
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
240
627
|
const state = this.state;
|
|
628
|
+
const cleanups = state === "live" ? this.takeClosing() : [];
|
|
241
629
|
this.state = "gone";
|
|
630
|
+
this.opening.clear();
|
|
631
|
+
const failures: unknown[] = [];
|
|
632
|
+
// (what waited to go in: what a framework placed in it and took back, alive, goes with it. Also
|
|
633
|
+
// while it opens: a child that disposed it stopped the others going in)
|
|
634
|
+
// (one at a time: a cleanup of one may take another away first, which then is no longer here)
|
|
635
|
+
for (let child = this.initial.shift(); child; child = this.initial.shift()) {
|
|
636
|
+
const next = child;
|
|
637
|
+
attempt(failures, () => {
|
|
638
|
+
next.forgetTree();
|
|
639
|
+
next.dispose();
|
|
640
|
+
});
|
|
641
|
+
}
|
|
242
642
|
if (state !== "live") {
|
|
643
|
+
this.destroyKeptNative();
|
|
644
|
+
throwFirst(failures);
|
|
243
645
|
return;
|
|
244
646
|
}
|
|
647
|
+
// (what it holds is not shown from now: a window a cleanup adds in it does not open)
|
|
648
|
+
this.content?.setShown(false);
|
|
649
|
+
this.island?.setShown(false);
|
|
245
650
|
if (this.isOpen) {
|
|
246
651
|
// (the window destroys what it holds: those of registered tags are theirs)
|
|
247
652
|
this.content?.releaseAdopted();
|
|
248
653
|
this.island?.releaseAdopted();
|
|
249
654
|
}
|
|
250
|
-
this.content?.disposeDetached();
|
|
251
|
-
this.island?.disposeDetached();
|
|
655
|
+
attempt(failures, () => this.content?.disposeDetached());
|
|
656
|
+
attempt(failures, () => this.island?.disposeDetached());
|
|
657
|
+
// (what it asked of Raven goes: a window forgotten first has none, one disposed directly still does)
|
|
658
|
+
closeEvents.set(this.id, undefined, false);
|
|
659
|
+
closeEvents.notice(this.id, undefined, false);
|
|
252
660
|
if (this.isOpen) {
|
|
253
661
|
r.Rwindow_destroy(this.id);
|
|
254
662
|
}
|
|
663
|
+
// (what the application put in the content, kept while it was closed, and not put back: opening failed)
|
|
664
|
+
this.destroyKeptNative();
|
|
665
|
+
// (last, as for close: a cleanup that closes or disposes the window finds it gone)
|
|
666
|
+
for (const cleanup of cleanups) {
|
|
667
|
+
attempt(failures, cleanup);
|
|
668
|
+
}
|
|
669
|
+
throwFirst(failures);
|
|
255
670
|
}
|
|
256
671
|
}
|
package/src/react/host-config.ts
CHANGED
|
@@ -125,7 +125,7 @@ const hostConfig: RavenHostConfig = {
|
|
|
125
125
|
bindToConsole: (_method, args) => console.log.bind(console, ...args),
|
|
126
126
|
getSuspendedCommitReason: () => null,
|
|
127
127
|
resetFormInstance: () => {},
|
|
128
|
-
rendererVersion: "0.1.
|
|
128
|
+
rendererVersion: "0.1.1",
|
|
129
129
|
rendererPackageName: "raven-tsx-react",
|
|
130
130
|
extraDevToolsConfig: null,
|
|
131
131
|
};
|