react-x11 2.33.0 → 2.35.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/package.json +2 -2
- package/src/cocoa/promotion.js +21 -6
- package/src/cocoa/sprites.js +217 -32
- package/src/node.d.ts +10 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-x11",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.35.0",
|
|
4
4
|
"description": "react renderer with X11 as a target",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
"yoga-layout": "^3.2.1"
|
|
110
110
|
},
|
|
111
111
|
"optionalDependencies": {
|
|
112
|
-
"@windowkit/appkit": "^0.
|
|
112
|
+
"@windowkit/appkit": "^0.22.0",
|
|
113
113
|
"@windowkit/wayland": "^3.1.1",
|
|
114
114
|
"@windowkit/win32": "^0.0.6",
|
|
115
115
|
"dbus-native": "^0.16.0",
|
package/src/cocoa/promotion.js
CHANGED
|
@@ -79,6 +79,11 @@ const rectsOverlap = (a, b) =>
|
|
|
79
79
|
a.y < b.y + b.height &&
|
|
80
80
|
b.y < a.y + a.height;
|
|
81
81
|
|
|
82
|
+
// `a` cut to `b`: their overlap, or a clip of no size where they have none,
|
|
83
|
+
// which hides what it holds rather than holding it whole
|
|
84
|
+
const cutTo = (a, b) =>
|
|
85
|
+
intersectRects(a, b) ?? { x: b.x, y: b.y, width: 0, height: 0 };
|
|
86
|
+
|
|
82
87
|
const containsRect = (outer, inner) =>
|
|
83
88
|
inner.x >= outer.x &&
|
|
84
89
|
inner.y >= outer.y &&
|
|
@@ -615,13 +620,23 @@ export class CocoaPromotion {
|
|
|
615
620
|
!(parent.style?.borderRadius > 0)
|
|
616
621
|
) {
|
|
617
622
|
const inner = insetRect(parent.abs, borderReach(parent));
|
|
618
|
-
// a clip with round corners cut again is no box's shape
|
|
619
|
-
|
|
620
|
-
|
|
623
|
+
// a clip with round corners cut again is no one box's shape: the
|
|
624
|
+
// square ones go in a box of their own around it (`outer`)
|
|
625
|
+
if (cut.radius > 0 && cut.clip) {
|
|
626
|
+
if (cut.outer || !containsRect(inner, cut.clip)) {
|
|
627
|
+
cut.outer = cut.outer ? cutTo(cut.outer, inner) : inner;
|
|
628
|
+
}
|
|
629
|
+
} else {
|
|
630
|
+
cut.clip = cut.clip ? cutTo(cut.clip, inner) : inner;
|
|
631
|
+
}
|
|
632
|
+
bounds = intersectRects(bounds, cut.clip);
|
|
633
|
+
if (bounds && cut.outer) bounds = intersectRects(bounds, cut.outer);
|
|
634
|
+
// nothing of the part shows: its layer is kept, cut to show nothing,
|
|
635
|
+
// and there is nothing it could be under or over until it does
|
|
636
|
+
if (!bounds) {
|
|
637
|
+
cut.bounds = null;
|
|
638
|
+
return true;
|
|
621
639
|
}
|
|
622
|
-
cut.clip = cut.clip ? intersectRects(cut.clip, inner) : inner;
|
|
623
|
-
bounds = cut.clip && intersectRects(bounds, cut.clip);
|
|
624
|
-
if (!bounds) return false;
|
|
625
640
|
}
|
|
626
641
|
const order = parent.paintOrder();
|
|
627
642
|
for (let j = order.indexOf(n) + 1; j < order.length; j++) {
|
package/src/cocoa/sprites.js
CHANGED
|
@@ -28,11 +28,27 @@
|
|
|
28
28
|
// delay a part joined half way through needs, so that is the bridge sprites
|
|
29
29
|
// ask for: on an older one nothing is asked and every part is drawn by its
|
|
30
30
|
// element.
|
|
31
|
+
//
|
|
32
|
+
// A part none of which shows — scrolled out of its pane, or outside its own
|
|
33
|
+
// clip — keeps its layer, cut to show nothing, up to a budget of raster for
|
|
34
|
+
// such parts: its element's clock would otherwise run for it, and it is
|
|
35
|
+
// already where its animation has it the frame a scroll brings it back. A
|
|
36
|
+
// clip with round corners that a square ancestor cuts again is a box of its
|
|
37
|
+
// own inside a box of the square one's.
|
|
38
|
+
//
|
|
39
|
+
// A part may be inside another of its element's (`parent`): a spinner in a
|
|
40
|
+
// card that fades. Its layer is lifted inside the parent's, so it fades,
|
|
41
|
+
// turns and is cut with it, and the parent's raster leaves it out — the
|
|
42
|
+
// presenter hands the parent's `paint` the keys of its children lifted with
|
|
43
|
+
// it, and paints it again whenever they change, in the frame they do. The
|
|
44
|
+
// parent was asked about everywhere it can be, so a child is asked about
|
|
45
|
+
// nothing more where it stays inside the parent's raster, and is its
|
|
46
|
+
// element's to draw where it does not, or where its parent is not lifted.
|
|
31
47
|
|
|
32
48
|
import { RASTER_PAD, RasterState } from './presenter.js';
|
|
33
49
|
import { CocoaPlayer, PlayerLift } from './player.js';
|
|
34
50
|
import { VideoLift, canLiftVideo } from './video.js';
|
|
35
|
-
import { intersectRects } from '../nodes/rects.js';
|
|
51
|
+
import { intersectRects, rectContains } from '../nodes/rects.js';
|
|
36
52
|
import { DEV } from '../nodes/util.js';
|
|
37
53
|
import { isVideoFrames } from '../videoframes.js';
|
|
38
54
|
|
|
@@ -41,12 +57,29 @@ import { isVideoFrames } from '../videoframes.js';
|
|
|
41
57
|
// upload for every new version.
|
|
42
58
|
const MAX_SIDE = 8192;
|
|
43
59
|
|
|
60
|
+
// The raster a window keeps for parts that show nothing — scrolled out of
|
|
61
|
+
// their pane, or outside their own clip — in device pixels: 32 MB. Past it
|
|
62
|
+
// such a part is its element's, which runs it on its own clock.
|
|
63
|
+
const HIDDEN_BUDGET = 8 * 1024 * 1024;
|
|
64
|
+
|
|
44
65
|
// Sorted after the element's children, which its own drawing comes after
|
|
45
66
|
// (`Node.paint` paints the children first), and before its next sibling.
|
|
46
67
|
const AFTER_CHILDREN = Number.MAX_SAFE_INTEGER;
|
|
47
68
|
|
|
48
69
|
const IDENTITY = Object.freeze([1, 0, 0, 1, 0, 0]);
|
|
49
70
|
|
|
71
|
+
const ORIGIN = Object.freeze({ x: 0, y: 0 });
|
|
72
|
+
|
|
73
|
+
// what a part with no part lifted inside it is handed
|
|
74
|
+
const NO_KIDS = Object.freeze(new Set());
|
|
75
|
+
|
|
76
|
+
// keyframes already found good, by the property they were found good for
|
|
77
|
+
const CHECKED = new WeakMap();
|
|
78
|
+
|
|
79
|
+
// where a part can be about its origin, by its one transform animation's
|
|
80
|
+
// keyframes, and what that was worked out for (`extentOf`)
|
|
81
|
+
const EXTENTS = new WeakMap();
|
|
82
|
+
|
|
50
83
|
// what a part's raster holds before its first paint
|
|
51
84
|
const UNPAINTED = Symbol('unpainted');
|
|
52
85
|
|
|
@@ -85,8 +118,13 @@ function isAnimation(a) {
|
|
|
85
118
|
if (!finite(a.duration) || !(a.duration > 0)) return false;
|
|
86
119
|
const values = a.values;
|
|
87
120
|
if (!Array.isArray(values) || values.length < 2) return false;
|
|
88
|
-
|
|
89
|
-
|
|
121
|
+
// an element hands the same keyframes over from frame to frame: they
|
|
122
|
+
// are read once
|
|
123
|
+
if (CHECKED.get(values) !== a.property) {
|
|
124
|
+
const value = a.property === 'opacity' ? finite : isMatrix;
|
|
125
|
+
if (!values.every(value)) return false;
|
|
126
|
+
CHECKED.set(values, a.property);
|
|
127
|
+
}
|
|
90
128
|
if (a.keyTimes !== undefined) {
|
|
91
129
|
const t = a.keyTimes;
|
|
92
130
|
if (!Array.isArray(t) || t.length !== values.length || t[0] !== 0) {
|
|
@@ -127,6 +165,9 @@ function isSprite(sprite) {
|
|
|
127
165
|
}
|
|
128
166
|
if (sprite.reach !== undefined && !isRect(sprite.reach)) return false;
|
|
129
167
|
if (sprite.clip !== undefined && !isRect(sprite.clip)) return false;
|
|
168
|
+
if (sprite.parent !== undefined && typeof sprite.parent !== 'string') {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
130
171
|
if (
|
|
131
172
|
sprite.clipRadius !== undefined &&
|
|
132
173
|
!(finite(sprite.clipRadius) && sprite.clipRadius >= 0)
|
|
@@ -210,11 +251,38 @@ function unionRect(a, b) {
|
|
|
210
251
|
function extentOf(sprite) {
|
|
211
252
|
const reach = sprite.reach ?? sprite.rect;
|
|
212
253
|
const origin = originOf(sprite);
|
|
213
|
-
|
|
254
|
+
const m0 = sprite.transform ?? IDENTITY;
|
|
255
|
+
// About its origin, where it can be is the same for a part that only
|
|
256
|
+
// moved: kept by its keyframes, which an element hands over from frame
|
|
257
|
+
// to frame, rather than every keyframe mapped every frame.
|
|
258
|
+
const rel = {
|
|
259
|
+
x: reach.x - origin.x,
|
|
260
|
+
y: reach.y - origin.y,
|
|
261
|
+
width: reach.width,
|
|
262
|
+
height: reach.height,
|
|
263
|
+
};
|
|
264
|
+
let turns = null;
|
|
214
265
|
for (const a of sprite.animations ?? []) {
|
|
215
266
|
if (a.property !== 'transform') continue;
|
|
216
|
-
|
|
267
|
+
turns = turns === null ? a.values : undefined;
|
|
268
|
+
}
|
|
269
|
+
const key = `${rel.x},${rel.y},${rel.width},${rel.height},${m0.join(',')}`;
|
|
270
|
+
let about = turns ? EXTENTS.get(turns) : null;
|
|
271
|
+
if (about?.key !== key) {
|
|
272
|
+
let box = mapRect(rel, m0, ORIGIN);
|
|
273
|
+
for (const a of sprite.animations ?? []) {
|
|
274
|
+
if (a.property !== 'transform') continue;
|
|
275
|
+
for (const m of a.values) box = unionRect(box, mapRect(rel, m, ORIGIN));
|
|
276
|
+
}
|
|
277
|
+
about = { key, box };
|
|
278
|
+
if (turns) EXTENTS.set(turns, about);
|
|
217
279
|
}
|
|
280
|
+
const box = {
|
|
281
|
+
x: about.box.x + origin.x,
|
|
282
|
+
y: about.box.y + origin.y,
|
|
283
|
+
width: about.box.width,
|
|
284
|
+
height: about.box.height,
|
|
285
|
+
};
|
|
218
286
|
const x = Math.floor(box.x) - RASTER_PAD;
|
|
219
287
|
const y = Math.floor(box.y) - RASTER_PAD;
|
|
220
288
|
return {
|
|
@@ -315,6 +383,8 @@ export class SpriteLayers {
|
|
|
315
383
|
this.hosts.delete(host);
|
|
316
384
|
if (!host.destroyed) changed.add(host);
|
|
317
385
|
}
|
|
386
|
+
// the raster kept for parts that show nothing, this frame
|
|
387
|
+
let hidden = 0;
|
|
318
388
|
for (const [host, list] of offers) {
|
|
319
389
|
const was = this.hosts.get(host);
|
|
320
390
|
const now = new Map();
|
|
@@ -331,6 +401,15 @@ export class SpriteLayers {
|
|
|
331
401
|
continue;
|
|
332
402
|
}
|
|
333
403
|
if (now.has(sprite.key)) continue;
|
|
404
|
+
// inside another of the element's parts: lifted with it or not at
|
|
405
|
+
// all, its parent earlier in the list, and neither of them a source
|
|
406
|
+
let parent = null;
|
|
407
|
+
if (sprite.parent !== undefined) {
|
|
408
|
+
parent = now.get(sprite.parent) ?? null;
|
|
409
|
+
if (!parent || parent.contents !== null || sprite.contents != null) {
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
334
413
|
const contents = sprite.contents ?? null;
|
|
335
414
|
// a source this bridge cannot show: the element draws it
|
|
336
415
|
if (contents !== null && !this._canShow(contents)) continue;
|
|
@@ -352,32 +431,69 @@ export class SpriteLayers {
|
|
|
352
431
|
};
|
|
353
432
|
const extent = extentOf(sprite);
|
|
354
433
|
const bounds = cut.clip ? intersectRects(extent, cut.clip) : extent;
|
|
355
|
-
if (
|
|
434
|
+
if (parent) {
|
|
435
|
+
// the parent was asked about everywhere it can be, and its layer
|
|
436
|
+
// carries this one: nothing more to ask where it stays inside the
|
|
437
|
+
// parent's raster, and its element draws it where it does not
|
|
438
|
+
if (bounds && !rectContains(rasterRectOf(parent.sprite), bounds)) {
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
cut.bounds = bounds;
|
|
442
|
+
} else if (!bounds) {
|
|
443
|
+
// its own clip shows nothing of it
|
|
444
|
+
cut.bounds = null;
|
|
445
|
+
} else if (!this.promotion._clear(host, bounds, cut)) {
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
356
448
|
const shown = cut.bounds;
|
|
449
|
+
// A part that shows nothing stays on its layer, cut to show nothing,
|
|
450
|
+
// so that its element's clock is not kept running for it, and is
|
|
451
|
+
// where it should be the frame a scroll brings it into view — but
|
|
452
|
+
// for a source, and past the budget of raster kept for such parts.
|
|
453
|
+
if (!shown) {
|
|
454
|
+
if (contents !== null) continue;
|
|
455
|
+
const px = raster.width * raster.height;
|
|
456
|
+
if (hidden + px > HIDDEN_BUDGET) continue;
|
|
457
|
+
hidden += px;
|
|
458
|
+
}
|
|
357
459
|
let state = was?.get(sprite.key);
|
|
358
460
|
// a clip that comes or goes is a layer in a box or out of one: lifted
|
|
359
461
|
// again, rather than a running layer moved between parents — and so
|
|
360
|
-
// is a part that shows another source, or stops painting to show
|
|
462
|
+
// is a part that shows another source, or stops painting to show
|
|
463
|
+
// one, and one in another layer than it was
|
|
361
464
|
if (
|
|
362
465
|
state &&
|
|
363
466
|
(Boolean(state.clip) !== Boolean(cut.clip) ||
|
|
364
|
-
state.
|
|
467
|
+
Boolean(state.outer) !== Boolean(cut.outer) ||
|
|
468
|
+
state.contents !== contents ||
|
|
469
|
+
state.parent !== parent)
|
|
365
470
|
) {
|
|
366
471
|
this._drop(state, root);
|
|
367
472
|
state = null;
|
|
368
473
|
}
|
|
369
474
|
const lifting = !state;
|
|
370
475
|
if (lifting) {
|
|
371
|
-
state = this._lift(
|
|
476
|
+
state = this._lift(
|
|
477
|
+
host,
|
|
478
|
+
sprite.key,
|
|
479
|
+
cut.clip !== null,
|
|
480
|
+
contents,
|
|
481
|
+
parent,
|
|
482
|
+
Boolean(cut.outer),
|
|
483
|
+
);
|
|
372
484
|
}
|
|
373
485
|
state.sprite = sprite;
|
|
374
486
|
state.clip = cut.clip;
|
|
487
|
+
state.outer = cut.outer ?? null;
|
|
375
488
|
state.radius = cut.radius;
|
|
376
|
-
state.order = [...place, AFTER_CHILDREN, index];
|
|
489
|
+
state.order = parent ? null : [...place, AFTER_CHILDREN, index];
|
|
490
|
+
state.kids = [];
|
|
491
|
+
parent?.kids.push(state);
|
|
377
492
|
// the bitmap under where it shows repaints without it, from this
|
|
378
493
|
// frame on; where it went since, it is the element's own claims
|
|
379
|
-
// that move the hole
|
|
380
|
-
|
|
494
|
+
// that move the hole. A child is under its parent's layer, whose
|
|
495
|
+
// raster leaves it out instead (`sync`).
|
|
496
|
+
if (lifting && !parent && shown) this.promotion._claim(root, shown);
|
|
381
497
|
state.extent = shown;
|
|
382
498
|
now.set(sprite.key, state);
|
|
383
499
|
}
|
|
@@ -396,11 +512,12 @@ export class SpriteLayers {
|
|
|
396
512
|
return changed;
|
|
397
513
|
}
|
|
398
514
|
|
|
399
|
-
/** Every lifted part, with its place in the paint
|
|
515
|
+
/** Every lifted part above the bitmap, with its place in the paint
|
|
516
|
+
* order: not one inside another's layer, which goes with it (`sync`). */
|
|
400
517
|
entries() {
|
|
401
518
|
const out = [];
|
|
402
519
|
for (const lifted of this.hosts.values()) {
|
|
403
|
-
for (const state of lifted.values()) out.push(state);
|
|
520
|
+
for (const state of lifted.values()) if (!state.parent) out.push(state);
|
|
404
521
|
}
|
|
405
522
|
return out;
|
|
406
523
|
}
|
|
@@ -426,23 +543,41 @@ export class SpriteLayers {
|
|
|
426
543
|
return isVideoFrames(contents) && this.videos;
|
|
427
544
|
}
|
|
428
545
|
|
|
429
|
-
_lift(host, key, clipped, contents = null) {
|
|
546
|
+
_lift(host, key, clipped, contents = null, parent = null, outered = false) {
|
|
430
547
|
const layer = this.native.createLayer();
|
|
431
548
|
// a part cut to a clip is in a box of the clip's size that masks to it,
|
|
432
|
-
// and the box is what stands among the layers above the bitmap
|
|
549
|
+
// and the box is what stands among the layers above the bitmap — or
|
|
550
|
+
// among its parent's children, for a part inside another's layer. A
|
|
551
|
+
// clip with round corners that a square one cuts again is in a box of
|
|
552
|
+
// its own inside a box of the square one's (`outer`).
|
|
553
|
+
const above = parent ? parent.layer : this.promotion.rootVisual.layer;
|
|
433
554
|
let box = null;
|
|
555
|
+
let outerBox = null;
|
|
434
556
|
if (clipped) {
|
|
435
557
|
box = this.native.createLayer();
|
|
436
|
-
|
|
558
|
+
if (outered) {
|
|
559
|
+
outerBox = this.native.createLayer();
|
|
560
|
+
this.native.addSublayer(above, outerBox);
|
|
561
|
+
this.native.addSublayer(outerBox, box);
|
|
562
|
+
} else {
|
|
563
|
+
this.native.addSublayer(above, box);
|
|
564
|
+
}
|
|
437
565
|
this.native.addSublayer(box, layer);
|
|
438
566
|
} else {
|
|
439
|
-
this.native.addSublayer(
|
|
567
|
+
this.native.addSublayer(above, layer);
|
|
440
568
|
}
|
|
441
569
|
return {
|
|
442
570
|
host,
|
|
443
571
|
key,
|
|
444
572
|
layer,
|
|
445
573
|
box,
|
|
574
|
+
outerBox,
|
|
575
|
+
outerProps: {},
|
|
576
|
+
outer: null, // the square clips around a rounded one, device pixels
|
|
577
|
+
// the part whose layer this one is in, and the parts in this one's
|
|
578
|
+
parent,
|
|
579
|
+
kids: [],
|
|
580
|
+
paintedKids: '',
|
|
446
581
|
boxProps: {},
|
|
447
582
|
clip: null, // device pixels, window coordinates
|
|
448
583
|
radius: 0, // the clip's corners, device pixels
|
|
@@ -475,11 +610,15 @@ export class SpriteLayers {
|
|
|
475
610
|
* element drawing it again. */
|
|
476
611
|
_drop(state, root) {
|
|
477
612
|
state.lift?.release();
|
|
478
|
-
this.native.removeFromSuperlayer(
|
|
613
|
+
this.native.removeFromSuperlayer(
|
|
614
|
+
state.outerBox ?? state.box ?? state.layer,
|
|
615
|
+
);
|
|
479
616
|
state.raster.release(this.native);
|
|
480
617
|
for (const run of state.runs.values()) this._ends().delete(run.caId);
|
|
481
618
|
state.runs.clear();
|
|
482
|
-
|
|
619
|
+
// a child's is under its parent's layer, which paints it again or goes
|
|
620
|
+
// with it
|
|
621
|
+
if (!state.host.destroyed && state.extent && !state.parent) {
|
|
483
622
|
this.promotion._claim(root, state.extent);
|
|
484
623
|
}
|
|
485
624
|
}
|
|
@@ -491,28 +630,65 @@ export class SpriteLayers {
|
|
|
491
630
|
const rect = rasterRectOf(sprite);
|
|
492
631
|
const reach = sprite.reach ?? sprite.rect;
|
|
493
632
|
const phase = `${reach.x - Math.floor(reach.x)},${reach.y - Math.floor(reach.y)}`;
|
|
633
|
+
// the parts lifted inside it, which its raster leaves out
|
|
634
|
+
const kids = state.kids.map((kid) => kid.key).join('\n');
|
|
494
635
|
// painted again for a new version, and for a raster of another size or
|
|
495
|
-
// with the part at another fraction of a pixel
|
|
496
|
-
// is the layer's alone. A
|
|
636
|
+
// with the part at another fraction of a pixel, or with other parts
|
|
637
|
+
// lifted inside it; a move by whole pixels is the layer's alone. A
|
|
638
|
+
// source paints nothing: it shows itself.
|
|
497
639
|
if (
|
|
498
640
|
!state.lift &&
|
|
499
641
|
(state.painted === UNPAINTED ||
|
|
500
642
|
sprite.version !== state.painted ||
|
|
501
643
|
state.rect?.width !== rect.width ||
|
|
502
644
|
state.rect?.height !== rect.height ||
|
|
503
|
-
state.phase !== phase
|
|
645
|
+
state.phase !== phase ||
|
|
646
|
+
state.paintedKids !== kids)
|
|
504
647
|
) {
|
|
505
648
|
this._paint(state, sprite, rect);
|
|
506
649
|
state.phase = phase;
|
|
650
|
+
state.paintedKids = kids;
|
|
507
651
|
}
|
|
508
652
|
state.rect = rect;
|
|
509
653
|
const origin = originOf(sprite);
|
|
510
654
|
const m = sprite.transform ?? IDENTITY;
|
|
655
|
+
// where its layer's coordinates start: the window's corner, or the
|
|
656
|
+
// corner of its parent's raster, for a part inside another's layer
|
|
657
|
+
const base = state.parent?.rect ?? ORIGIN;
|
|
658
|
+
// in a box of the square clips around its own, where a square one cuts
|
|
659
|
+
// a rounded clip again: placed from the window's corner, at the part's
|
|
660
|
+
// place among the layers
|
|
661
|
+
const outer = state.outer;
|
|
662
|
+
if (state.outerBox) {
|
|
663
|
+
const frame = [
|
|
664
|
+
(outer.x - base.x) / s,
|
|
665
|
+
(outer.y - base.y) / s,
|
|
666
|
+
outer.width / s,
|
|
667
|
+
outer.height / s,
|
|
668
|
+
];
|
|
669
|
+
const was = state.outerProps;
|
|
670
|
+
if (!sameMatrix(was.frame, frame) || was.zPosition !== z) {
|
|
671
|
+
this.native.setLayerProps(state.outerBox, {
|
|
672
|
+
frame,
|
|
673
|
+
masksToBounds: true,
|
|
674
|
+
zPosition: z,
|
|
675
|
+
});
|
|
676
|
+
state.outerProps = { frame, zPosition: z };
|
|
677
|
+
}
|
|
678
|
+
}
|
|
511
679
|
// in its box, where it is cut to a clip: placed from the clip's corner,
|
|
512
|
-
// and the box at the part's place among the layers
|
|
680
|
+
// and the box at the part's place among the layers, or from the outer
|
|
681
|
+
// box's corner inside it
|
|
513
682
|
const clip = state.clip;
|
|
514
683
|
if (state.box) {
|
|
515
|
-
const
|
|
684
|
+
const from = state.outerBox ? outer : base;
|
|
685
|
+
const frame = [
|
|
686
|
+
(clip.x - from.x) / s,
|
|
687
|
+
(clip.y - from.y) / s,
|
|
688
|
+
clip.width / s,
|
|
689
|
+
clip.height / s,
|
|
690
|
+
];
|
|
691
|
+
const boxZ = state.outerBox ? 0 : z;
|
|
516
692
|
// its corners rounded where the clip's are: a circle's arc, as wide
|
|
517
693
|
// as the box allows
|
|
518
694
|
const cornerRadius =
|
|
@@ -520,20 +696,20 @@ export class SpriteLayers {
|
|
|
520
696
|
const was = state.boxProps;
|
|
521
697
|
if (
|
|
522
698
|
!sameMatrix(was.frame, frame) ||
|
|
523
|
-
was.zPosition !==
|
|
699
|
+
was.zPosition !== boxZ ||
|
|
524
700
|
was.cornerRadius !== cornerRadius
|
|
525
701
|
) {
|
|
526
702
|
this.native.setLayerProps(state.box, {
|
|
527
703
|
frame,
|
|
528
704
|
masksToBounds: true,
|
|
529
705
|
cornerRadius,
|
|
530
|
-
zPosition:
|
|
706
|
+
zPosition: boxZ,
|
|
531
707
|
});
|
|
532
|
-
state.boxProps = { frame, zPosition:
|
|
708
|
+
state.boxProps = { frame, zPosition: boxZ, cornerRadius };
|
|
533
709
|
}
|
|
534
710
|
}
|
|
535
|
-
const ox = state.box ? clip.x :
|
|
536
|
-
const oy = state.box ? clip.y :
|
|
711
|
+
const ox = state.box ? clip.x : base.x;
|
|
712
|
+
const oy = state.box ? clip.y : base.y;
|
|
537
713
|
const next = {
|
|
538
714
|
bounds: [0, 0, rect.width / s, rect.height / s],
|
|
539
715
|
anchorPoint: [
|
|
@@ -570,6 +746,10 @@ export class SpriteLayers {
|
|
|
570
746
|
state.lift?.layout?.(next.bounds);
|
|
571
747
|
// after the model went out, in the same transaction
|
|
572
748
|
this._syncAnimations(state, sprite);
|
|
749
|
+
// the parts inside it, in the element's order, over its raster
|
|
750
|
+
for (let i = 0; i < state.kids.length; i++) {
|
|
751
|
+
this.sync(state.kids[i], i + 1);
|
|
752
|
+
}
|
|
573
753
|
}
|
|
574
754
|
|
|
575
755
|
_paint(state, sprite, rect) {
|
|
@@ -586,7 +766,10 @@ export class SpriteLayers {
|
|
|
586
766
|
ctx.translate(-rect.x, -rect.y);
|
|
587
767
|
// a part paints whole: there is no pass to cull it by
|
|
588
768
|
if (root) root._paintDamage = null;
|
|
589
|
-
sprite.paint(
|
|
769
|
+
sprite.paint(
|
|
770
|
+
ctx,
|
|
771
|
+
state.kids.length ? new Set(state.kids.map((kid) => kid.key)) : NO_KIDS,
|
|
772
|
+
);
|
|
590
773
|
} catch (err) {
|
|
591
774
|
this._warn(
|
|
592
775
|
state.host,
|
|
@@ -674,7 +857,9 @@ export class SpriteLayers {
|
|
|
674
857
|
for (const lifted of this.hosts.values()) {
|
|
675
858
|
for (const state of lifted.values()) {
|
|
676
859
|
state.lift?.release();
|
|
677
|
-
this.native.removeFromSuperlayer(
|
|
860
|
+
this.native.removeFromSuperlayer(
|
|
861
|
+
state.outerBox ?? state.box ?? state.layer,
|
|
862
|
+
);
|
|
678
863
|
state.raster.release(this.native);
|
|
679
864
|
for (const run of state.runs.values()) this._ends().delete(run.caId);
|
|
680
865
|
}
|
package/src/node.d.ts
CHANGED
|
@@ -297,8 +297,16 @@ export interface Sprite {
|
|
|
297
297
|
* cannot take both. */
|
|
298
298
|
clipRadius?: number;
|
|
299
299
|
/** Draws the part where it is in the window, as `paint` would, but at
|
|
300
|
-
* opacity 1 and untransformed
|
|
301
|
-
|
|
300
|
+
* opacity 1 and untransformed — and without the parts lifted inside it
|
|
301
|
+
* (`parent`), whose keys the presenter hands it as `children`. A part
|
|
302
|
+
* with `contents` has none. */
|
|
303
|
+
paint?(ctx: Context2D, children?: ReadonlySet<string>): void;
|
|
304
|
+
/** The key of another of the element's parts this one is inside, earlier
|
|
305
|
+
* in the list: its layer goes in that part's layer, fading, turning and
|
|
306
|
+
* cut with it, and is lifted only with it, and only where everywhere it
|
|
307
|
+
* can be is inside that part's raster. Its `rect`, `origin` and `clip`
|
|
308
|
+
* are the window's, with its parent untransformed. */
|
|
309
|
+
parent?: string;
|
|
302
310
|
/**
|
|
303
311
|
* A source that shows itself on the part's layer instead of a paint — a
|
|
304
312
|
* `VideoFrames` sink, whose every push reaches the layer with no frame of
|