react-x11 2.34.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 +128 -18
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
|
@@ -29,6 +29,13 @@
|
|
|
29
29
|
// ask for: on an older one nothing is asked and every part is drawn by its
|
|
30
30
|
// element.
|
|
31
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
|
+
//
|
|
32
39
|
// A part may be inside another of its element's (`parent`): a spinner in a
|
|
33
40
|
// card that fades. Its layer is lifted inside the parent's, so it fades,
|
|
34
41
|
// turns and is cut with it, and the parent's raster leaves it out — the
|
|
@@ -50,6 +57,11 @@ import { isVideoFrames } from '../videoframes.js';
|
|
|
50
57
|
// upload for every new version.
|
|
51
58
|
const MAX_SIDE = 8192;
|
|
52
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
|
+
|
|
53
65
|
// Sorted after the element's children, which its own drawing comes after
|
|
54
66
|
// (`Node.paint` paints the children first), and before its next sibling.
|
|
55
67
|
const AFTER_CHILDREN = Number.MAX_SAFE_INTEGER;
|
|
@@ -61,6 +73,13 @@ const ORIGIN = Object.freeze({ x: 0, y: 0 });
|
|
|
61
73
|
// what a part with no part lifted inside it is handed
|
|
62
74
|
const NO_KIDS = Object.freeze(new Set());
|
|
63
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
|
+
|
|
64
83
|
// what a part's raster holds before its first paint
|
|
65
84
|
const UNPAINTED = Symbol('unpainted');
|
|
66
85
|
|
|
@@ -99,8 +118,13 @@ function isAnimation(a) {
|
|
|
99
118
|
if (!finite(a.duration) || !(a.duration > 0)) return false;
|
|
100
119
|
const values = a.values;
|
|
101
120
|
if (!Array.isArray(values) || values.length < 2) return false;
|
|
102
|
-
|
|
103
|
-
|
|
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
|
+
}
|
|
104
128
|
if (a.keyTimes !== undefined) {
|
|
105
129
|
const t = a.keyTimes;
|
|
106
130
|
if (!Array.isArray(t) || t.length !== values.length || t[0] !== 0) {
|
|
@@ -227,11 +251,38 @@ function unionRect(a, b) {
|
|
|
227
251
|
function extentOf(sprite) {
|
|
228
252
|
const reach = sprite.reach ?? sprite.rect;
|
|
229
253
|
const origin = originOf(sprite);
|
|
230
|
-
|
|
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;
|
|
231
265
|
for (const a of sprite.animations ?? []) {
|
|
232
266
|
if (a.property !== 'transform') continue;
|
|
233
|
-
|
|
267
|
+
turns = turns === null ? a.values : undefined;
|
|
234
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);
|
|
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
|
+
};
|
|
235
286
|
const x = Math.floor(box.x) - RASTER_PAD;
|
|
236
287
|
const y = Math.floor(box.y) - RASTER_PAD;
|
|
237
288
|
return {
|
|
@@ -332,6 +383,8 @@ export class SpriteLayers {
|
|
|
332
383
|
this.hosts.delete(host);
|
|
333
384
|
if (!host.destroyed) changed.add(host);
|
|
334
385
|
}
|
|
386
|
+
// the raster kept for parts that show nothing, this frame
|
|
387
|
+
let hidden = 0;
|
|
335
388
|
for (const [host, list] of offers) {
|
|
336
389
|
const was = this.hosts.get(host);
|
|
337
390
|
const now = new Map();
|
|
@@ -378,17 +431,31 @@ export class SpriteLayers {
|
|
|
378
431
|
};
|
|
379
432
|
const extent = extentOf(sprite);
|
|
380
433
|
const bounds = cut.clip ? intersectRects(extent, cut.clip) : extent;
|
|
381
|
-
if (!bounds) continue;
|
|
382
434
|
if (parent) {
|
|
383
435
|
// the parent was asked about everywhere it can be, and its layer
|
|
384
436
|
// carries this one: nothing more to ask where it stays inside the
|
|
385
437
|
// parent's raster, and its element draws it where it does not
|
|
386
|
-
if (!rectContains(rasterRectOf(parent.sprite), bounds))
|
|
438
|
+
if (bounds && !rectContains(rasterRectOf(parent.sprite), bounds)) {
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
387
441
|
cut.bounds = bounds;
|
|
442
|
+
} else if (!bounds) {
|
|
443
|
+
// its own clip shows nothing of it
|
|
444
|
+
cut.bounds = null;
|
|
388
445
|
} else if (!this.promotion._clear(host, bounds, cut)) {
|
|
389
446
|
continue;
|
|
390
447
|
}
|
|
391
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
|
+
}
|
|
392
459
|
let state = was?.get(sprite.key);
|
|
393
460
|
// a clip that comes or goes is a layer in a box or out of one: lifted
|
|
394
461
|
// again, rather than a running layer moved between parents — and so
|
|
@@ -397,6 +464,7 @@ export class SpriteLayers {
|
|
|
397
464
|
if (
|
|
398
465
|
state &&
|
|
399
466
|
(Boolean(state.clip) !== Boolean(cut.clip) ||
|
|
467
|
+
Boolean(state.outer) !== Boolean(cut.outer) ||
|
|
400
468
|
state.contents !== contents ||
|
|
401
469
|
state.parent !== parent)
|
|
402
470
|
) {
|
|
@@ -411,10 +479,12 @@ export class SpriteLayers {
|
|
|
411
479
|
cut.clip !== null,
|
|
412
480
|
contents,
|
|
413
481
|
parent,
|
|
482
|
+
Boolean(cut.outer),
|
|
414
483
|
);
|
|
415
484
|
}
|
|
416
485
|
state.sprite = sprite;
|
|
417
486
|
state.clip = cut.clip;
|
|
487
|
+
state.outer = cut.outer ?? null;
|
|
418
488
|
state.radius = cut.radius;
|
|
419
489
|
state.order = parent ? null : [...place, AFTER_CHILDREN, index];
|
|
420
490
|
state.kids = [];
|
|
@@ -423,7 +493,7 @@ export class SpriteLayers {
|
|
|
423
493
|
// frame on; where it went since, it is the element's own claims
|
|
424
494
|
// that move the hole. A child is under its parent's layer, whose
|
|
425
495
|
// raster leaves it out instead (`sync`).
|
|
426
|
-
if (lifting && !parent) this.promotion._claim(root, shown);
|
|
496
|
+
if (lifting && !parent && shown) this.promotion._claim(root, shown);
|
|
427
497
|
state.extent = shown;
|
|
428
498
|
now.set(sprite.key, state);
|
|
429
499
|
}
|
|
@@ -473,16 +543,25 @@ export class SpriteLayers {
|
|
|
473
543
|
return isVideoFrames(contents) && this.videos;
|
|
474
544
|
}
|
|
475
545
|
|
|
476
|
-
_lift(host, key, clipped, contents = null, parent = null) {
|
|
546
|
+
_lift(host, key, clipped, contents = null, parent = null, outered = false) {
|
|
477
547
|
const layer = this.native.createLayer();
|
|
478
548
|
// a part cut to a clip is in a box of the clip's size that masks to it,
|
|
479
549
|
// 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
|
|
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`).
|
|
481
553
|
const above = parent ? parent.layer : this.promotion.rootVisual.layer;
|
|
482
554
|
let box = null;
|
|
555
|
+
let outerBox = null;
|
|
483
556
|
if (clipped) {
|
|
484
557
|
box = this.native.createLayer();
|
|
485
|
-
|
|
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
|
+
}
|
|
486
565
|
this.native.addSublayer(box, layer);
|
|
487
566
|
} else {
|
|
488
567
|
this.native.addSublayer(above, layer);
|
|
@@ -492,6 +571,9 @@ export class SpriteLayers {
|
|
|
492
571
|
key,
|
|
493
572
|
layer,
|
|
494
573
|
box,
|
|
574
|
+
outerBox,
|
|
575
|
+
outerProps: {},
|
|
576
|
+
outer: null, // the square clips around a rounded one, device pixels
|
|
495
577
|
// the part whose layer this one is in, and the parts in this one's
|
|
496
578
|
parent,
|
|
497
579
|
kids: [],
|
|
@@ -528,7 +610,9 @@ export class SpriteLayers {
|
|
|
528
610
|
* element drawing it again. */
|
|
529
611
|
_drop(state, root) {
|
|
530
612
|
state.lift?.release();
|
|
531
|
-
this.native.removeFromSuperlayer(
|
|
613
|
+
this.native.removeFromSuperlayer(
|
|
614
|
+
state.outerBox ?? state.box ?? state.layer,
|
|
615
|
+
);
|
|
532
616
|
state.raster.release(this.native);
|
|
533
617
|
for (const run of state.runs.values()) this._ends().delete(run.caId);
|
|
534
618
|
state.runs.clear();
|
|
@@ -571,16 +655,40 @@ export class SpriteLayers {
|
|
|
571
655
|
// where its layer's coordinates start: the window's corner, or the
|
|
572
656
|
// corner of its parent's raster, for a part inside another's layer
|
|
573
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
|
+
}
|
|
574
679
|
// in its box, where it is cut to a clip: placed from the clip's corner,
|
|
575
|
-
// 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
|
|
576
682
|
const clip = state.clip;
|
|
577
683
|
if (state.box) {
|
|
684
|
+
const from = state.outerBox ? outer : base;
|
|
578
685
|
const frame = [
|
|
579
|
-
(clip.x -
|
|
580
|
-
(clip.y -
|
|
686
|
+
(clip.x - from.x) / s,
|
|
687
|
+
(clip.y - from.y) / s,
|
|
581
688
|
clip.width / s,
|
|
582
689
|
clip.height / s,
|
|
583
690
|
];
|
|
691
|
+
const boxZ = state.outerBox ? 0 : z;
|
|
584
692
|
// its corners rounded where the clip's are: a circle's arc, as wide
|
|
585
693
|
// as the box allows
|
|
586
694
|
const cornerRadius =
|
|
@@ -588,16 +696,16 @@ export class SpriteLayers {
|
|
|
588
696
|
const was = state.boxProps;
|
|
589
697
|
if (
|
|
590
698
|
!sameMatrix(was.frame, frame) ||
|
|
591
|
-
was.zPosition !==
|
|
699
|
+
was.zPosition !== boxZ ||
|
|
592
700
|
was.cornerRadius !== cornerRadius
|
|
593
701
|
) {
|
|
594
702
|
this.native.setLayerProps(state.box, {
|
|
595
703
|
frame,
|
|
596
704
|
masksToBounds: true,
|
|
597
705
|
cornerRadius,
|
|
598
|
-
zPosition:
|
|
706
|
+
zPosition: boxZ,
|
|
599
707
|
});
|
|
600
|
-
state.boxProps = { frame, zPosition:
|
|
708
|
+
state.boxProps = { frame, zPosition: boxZ, cornerRadius };
|
|
601
709
|
}
|
|
602
710
|
}
|
|
603
711
|
const ox = state.box ? clip.x : base.x;
|
|
@@ -749,7 +857,9 @@ export class SpriteLayers {
|
|
|
749
857
|
for (const lifted of this.hosts.values()) {
|
|
750
858
|
for (const state of lifted.values()) {
|
|
751
859
|
state.lift?.release();
|
|
752
|
-
this.native.removeFromSuperlayer(
|
|
860
|
+
this.native.removeFromSuperlayer(
|
|
861
|
+
state.outerBox ?? state.box ?? state.layer,
|
|
862
|
+
);
|
|
753
863
|
state.raster.release(this.native);
|
|
754
864
|
for (const run of state.runs.values()) this._ends().delete(run.caId);
|
|
755
865
|
}
|