react-x11 2.33.0 → 2.34.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 +1 -1
- package/src/cocoa/sprites.js +97 -22
- package/src/node.d.ts +10 -2
package/package.json
CHANGED
package/src/cocoa/sprites.js
CHANGED
|
@@ -28,11 +28,20 @@
|
|
|
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 may be inside another of its element's (`parent`): a spinner in a
|
|
33
|
+
// card that fades. Its layer is lifted inside the parent's, so it fades,
|
|
34
|
+
// turns and is cut with it, and the parent's raster leaves it out — the
|
|
35
|
+
// presenter hands the parent's `paint` the keys of its children lifted with
|
|
36
|
+
// it, and paints it again whenever they change, in the frame they do. The
|
|
37
|
+
// parent was asked about everywhere it can be, so a child is asked about
|
|
38
|
+
// nothing more where it stays inside the parent's raster, and is its
|
|
39
|
+
// element's to draw where it does not, or where its parent is not lifted.
|
|
31
40
|
|
|
32
41
|
import { RASTER_PAD, RasterState } from './presenter.js';
|
|
33
42
|
import { CocoaPlayer, PlayerLift } from './player.js';
|
|
34
43
|
import { VideoLift, canLiftVideo } from './video.js';
|
|
35
|
-
import { intersectRects } from '../nodes/rects.js';
|
|
44
|
+
import { intersectRects, rectContains } from '../nodes/rects.js';
|
|
36
45
|
import { DEV } from '../nodes/util.js';
|
|
37
46
|
import { isVideoFrames } from '../videoframes.js';
|
|
38
47
|
|
|
@@ -47,6 +56,11 @@ const AFTER_CHILDREN = Number.MAX_SAFE_INTEGER;
|
|
|
47
56
|
|
|
48
57
|
const IDENTITY = Object.freeze([1, 0, 0, 1, 0, 0]);
|
|
49
58
|
|
|
59
|
+
const ORIGIN = Object.freeze({ x: 0, y: 0 });
|
|
60
|
+
|
|
61
|
+
// what a part with no part lifted inside it is handed
|
|
62
|
+
const NO_KIDS = Object.freeze(new Set());
|
|
63
|
+
|
|
50
64
|
// what a part's raster holds before its first paint
|
|
51
65
|
const UNPAINTED = Symbol('unpainted');
|
|
52
66
|
|
|
@@ -127,6 +141,9 @@ function isSprite(sprite) {
|
|
|
127
141
|
}
|
|
128
142
|
if (sprite.reach !== undefined && !isRect(sprite.reach)) return false;
|
|
129
143
|
if (sprite.clip !== undefined && !isRect(sprite.clip)) return false;
|
|
144
|
+
if (sprite.parent !== undefined && typeof sprite.parent !== 'string') {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
130
147
|
if (
|
|
131
148
|
sprite.clipRadius !== undefined &&
|
|
132
149
|
!(finite(sprite.clipRadius) && sprite.clipRadius >= 0)
|
|
@@ -331,6 +348,15 @@ export class SpriteLayers {
|
|
|
331
348
|
continue;
|
|
332
349
|
}
|
|
333
350
|
if (now.has(sprite.key)) continue;
|
|
351
|
+
// inside another of the element's parts: lifted with it or not at
|
|
352
|
+
// all, its parent earlier in the list, and neither of them a source
|
|
353
|
+
let parent = null;
|
|
354
|
+
if (sprite.parent !== undefined) {
|
|
355
|
+
parent = now.get(sprite.parent) ?? null;
|
|
356
|
+
if (!parent || parent.contents !== null || sprite.contents != null) {
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
334
360
|
const contents = sprite.contents ?? null;
|
|
335
361
|
// a source this bridge cannot show: the element draws it
|
|
336
362
|
if (contents !== null && !this._canShow(contents)) continue;
|
|
@@ -352,32 +378,52 @@ export class SpriteLayers {
|
|
|
352
378
|
};
|
|
353
379
|
const extent = extentOf(sprite);
|
|
354
380
|
const bounds = cut.clip ? intersectRects(extent, cut.clip) : extent;
|
|
355
|
-
if (!bounds
|
|
381
|
+
if (!bounds) continue;
|
|
382
|
+
if (parent) {
|
|
383
|
+
// the parent was asked about everywhere it can be, and its layer
|
|
384
|
+
// carries this one: nothing more to ask where it stays inside the
|
|
385
|
+
// parent's raster, and its element draws it where it does not
|
|
386
|
+
if (!rectContains(rasterRectOf(parent.sprite), bounds)) continue;
|
|
387
|
+
cut.bounds = bounds;
|
|
388
|
+
} else if (!this.promotion._clear(host, bounds, cut)) {
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
356
391
|
const shown = cut.bounds;
|
|
357
392
|
let state = was?.get(sprite.key);
|
|
358
393
|
// a clip that comes or goes is a layer in a box or out of one: lifted
|
|
359
394
|
// again, rather than a running layer moved between parents — and so
|
|
360
|
-
// is a part that shows another source, or stops painting to show
|
|
395
|
+
// is a part that shows another source, or stops painting to show
|
|
396
|
+
// one, and one in another layer than it was
|
|
361
397
|
if (
|
|
362
398
|
state &&
|
|
363
399
|
(Boolean(state.clip) !== Boolean(cut.clip) ||
|
|
364
|
-
state.contents !== contents
|
|
400
|
+
state.contents !== contents ||
|
|
401
|
+
state.parent !== parent)
|
|
365
402
|
) {
|
|
366
403
|
this._drop(state, root);
|
|
367
404
|
state = null;
|
|
368
405
|
}
|
|
369
406
|
const lifting = !state;
|
|
370
407
|
if (lifting) {
|
|
371
|
-
state = this._lift(
|
|
408
|
+
state = this._lift(
|
|
409
|
+
host,
|
|
410
|
+
sprite.key,
|
|
411
|
+
cut.clip !== null,
|
|
412
|
+
contents,
|
|
413
|
+
parent,
|
|
414
|
+
);
|
|
372
415
|
}
|
|
373
416
|
state.sprite = sprite;
|
|
374
417
|
state.clip = cut.clip;
|
|
375
418
|
state.radius = cut.radius;
|
|
376
|
-
state.order = [...place, AFTER_CHILDREN, index];
|
|
419
|
+
state.order = parent ? null : [...place, AFTER_CHILDREN, index];
|
|
420
|
+
state.kids = [];
|
|
421
|
+
parent?.kids.push(state);
|
|
377
422
|
// the bitmap under where it shows repaints without it, from this
|
|
378
423
|
// frame on; where it went since, it is the element's own claims
|
|
379
|
-
// that move the hole
|
|
380
|
-
|
|
424
|
+
// that move the hole. A child is under its parent's layer, whose
|
|
425
|
+
// raster leaves it out instead (`sync`).
|
|
426
|
+
if (lifting && !parent) this.promotion._claim(root, shown);
|
|
381
427
|
state.extent = shown;
|
|
382
428
|
now.set(sprite.key, state);
|
|
383
429
|
}
|
|
@@ -396,11 +442,12 @@ export class SpriteLayers {
|
|
|
396
442
|
return changed;
|
|
397
443
|
}
|
|
398
444
|
|
|
399
|
-
/** Every lifted part, with its place in the paint
|
|
445
|
+
/** Every lifted part above the bitmap, with its place in the paint
|
|
446
|
+
* order: not one inside another's layer, which goes with it (`sync`). */
|
|
400
447
|
entries() {
|
|
401
448
|
const out = [];
|
|
402
449
|
for (const lifted of this.hosts.values()) {
|
|
403
|
-
for (const state of lifted.values()) out.push(state);
|
|
450
|
+
for (const state of lifted.values()) if (!state.parent) out.push(state);
|
|
404
451
|
}
|
|
405
452
|
return out;
|
|
406
453
|
}
|
|
@@ -426,23 +473,29 @@ export class SpriteLayers {
|
|
|
426
473
|
return isVideoFrames(contents) && this.videos;
|
|
427
474
|
}
|
|
428
475
|
|
|
429
|
-
_lift(host, key, clipped, contents = null) {
|
|
476
|
+
_lift(host, key, clipped, contents = null, parent = null) {
|
|
430
477
|
const layer = this.native.createLayer();
|
|
431
478
|
// 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
|
|
479
|
+
// and the box is what stands among the layers above the bitmap — or
|
|
480
|
+
// among its parent's children, for a part inside another's layer
|
|
481
|
+
const above = parent ? parent.layer : this.promotion.rootVisual.layer;
|
|
433
482
|
let box = null;
|
|
434
483
|
if (clipped) {
|
|
435
484
|
box = this.native.createLayer();
|
|
436
|
-
this.native.addSublayer(
|
|
485
|
+
this.native.addSublayer(above, box);
|
|
437
486
|
this.native.addSublayer(box, layer);
|
|
438
487
|
} else {
|
|
439
|
-
this.native.addSublayer(
|
|
488
|
+
this.native.addSublayer(above, layer);
|
|
440
489
|
}
|
|
441
490
|
return {
|
|
442
491
|
host,
|
|
443
492
|
key,
|
|
444
493
|
layer,
|
|
445
494
|
box,
|
|
495
|
+
// the part whose layer this one is in, and the parts in this one's
|
|
496
|
+
parent,
|
|
497
|
+
kids: [],
|
|
498
|
+
paintedKids: '',
|
|
446
499
|
boxProps: {},
|
|
447
500
|
clip: null, // device pixels, window coordinates
|
|
448
501
|
radius: 0, // the clip's corners, device pixels
|
|
@@ -479,7 +532,9 @@ export class SpriteLayers {
|
|
|
479
532
|
state.raster.release(this.native);
|
|
480
533
|
for (const run of state.runs.values()) this._ends().delete(run.caId);
|
|
481
534
|
state.runs.clear();
|
|
482
|
-
|
|
535
|
+
// a child's is under its parent's layer, which paints it again or goes
|
|
536
|
+
// with it
|
|
537
|
+
if (!state.host.destroyed && state.extent && !state.parent) {
|
|
483
538
|
this.promotion._claim(root, state.extent);
|
|
484
539
|
}
|
|
485
540
|
}
|
|
@@ -491,28 +546,41 @@ export class SpriteLayers {
|
|
|
491
546
|
const rect = rasterRectOf(sprite);
|
|
492
547
|
const reach = sprite.reach ?? sprite.rect;
|
|
493
548
|
const phase = `${reach.x - Math.floor(reach.x)},${reach.y - Math.floor(reach.y)}`;
|
|
549
|
+
// the parts lifted inside it, which its raster leaves out
|
|
550
|
+
const kids = state.kids.map((kid) => kid.key).join('\n');
|
|
494
551
|
// 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
|
|
552
|
+
// with the part at another fraction of a pixel, or with other parts
|
|
553
|
+
// lifted inside it; a move by whole pixels is the layer's alone. A
|
|
554
|
+
// source paints nothing: it shows itself.
|
|
497
555
|
if (
|
|
498
556
|
!state.lift &&
|
|
499
557
|
(state.painted === UNPAINTED ||
|
|
500
558
|
sprite.version !== state.painted ||
|
|
501
559
|
state.rect?.width !== rect.width ||
|
|
502
560
|
state.rect?.height !== rect.height ||
|
|
503
|
-
state.phase !== phase
|
|
561
|
+
state.phase !== phase ||
|
|
562
|
+
state.paintedKids !== kids)
|
|
504
563
|
) {
|
|
505
564
|
this._paint(state, sprite, rect);
|
|
506
565
|
state.phase = phase;
|
|
566
|
+
state.paintedKids = kids;
|
|
507
567
|
}
|
|
508
568
|
state.rect = rect;
|
|
509
569
|
const origin = originOf(sprite);
|
|
510
570
|
const m = sprite.transform ?? IDENTITY;
|
|
571
|
+
// where its layer's coordinates start: the window's corner, or the
|
|
572
|
+
// corner of its parent's raster, for a part inside another's layer
|
|
573
|
+
const base = state.parent?.rect ?? ORIGIN;
|
|
511
574
|
// in its box, where it is cut to a clip: placed from the clip's corner,
|
|
512
575
|
// and the box at the part's place among the layers
|
|
513
576
|
const clip = state.clip;
|
|
514
577
|
if (state.box) {
|
|
515
|
-
const frame = [
|
|
578
|
+
const frame = [
|
|
579
|
+
(clip.x - base.x) / s,
|
|
580
|
+
(clip.y - base.y) / s,
|
|
581
|
+
clip.width / s,
|
|
582
|
+
clip.height / s,
|
|
583
|
+
];
|
|
516
584
|
// its corners rounded where the clip's are: a circle's arc, as wide
|
|
517
585
|
// as the box allows
|
|
518
586
|
const cornerRadius =
|
|
@@ -532,8 +600,8 @@ export class SpriteLayers {
|
|
|
532
600
|
state.boxProps = { frame, zPosition: z, cornerRadius };
|
|
533
601
|
}
|
|
534
602
|
}
|
|
535
|
-
const ox = state.box ? clip.x :
|
|
536
|
-
const oy = state.box ? clip.y :
|
|
603
|
+
const ox = state.box ? clip.x : base.x;
|
|
604
|
+
const oy = state.box ? clip.y : base.y;
|
|
537
605
|
const next = {
|
|
538
606
|
bounds: [0, 0, rect.width / s, rect.height / s],
|
|
539
607
|
anchorPoint: [
|
|
@@ -570,6 +638,10 @@ export class SpriteLayers {
|
|
|
570
638
|
state.lift?.layout?.(next.bounds);
|
|
571
639
|
// after the model went out, in the same transaction
|
|
572
640
|
this._syncAnimations(state, sprite);
|
|
641
|
+
// the parts inside it, in the element's order, over its raster
|
|
642
|
+
for (let i = 0; i < state.kids.length; i++) {
|
|
643
|
+
this.sync(state.kids[i], i + 1);
|
|
644
|
+
}
|
|
573
645
|
}
|
|
574
646
|
|
|
575
647
|
_paint(state, sprite, rect) {
|
|
@@ -586,7 +658,10 @@ export class SpriteLayers {
|
|
|
586
658
|
ctx.translate(-rect.x, -rect.y);
|
|
587
659
|
// a part paints whole: there is no pass to cull it by
|
|
588
660
|
if (root) root._paintDamage = null;
|
|
589
|
-
sprite.paint(
|
|
661
|
+
sprite.paint(
|
|
662
|
+
ctx,
|
|
663
|
+
state.kids.length ? new Set(state.kids.map((kid) => kid.key)) : NO_KIDS,
|
|
664
|
+
);
|
|
590
665
|
} catch (err) {
|
|
591
666
|
this._warn(
|
|
592
667
|
state.host,
|
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
|