react-x11 2.18.5 → 2.19.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 +3 -3
- package/src/Reconciler.js +9 -2
- package/src/backend/context2d.js +39 -2
- package/src/cocoa/app.js +1 -1
- package/src/cocoa/fonts.js +2 -1
- package/src/cocoa/presenter.js +1 -1
- package/src/decorations.js +1 -1
- package/src/fonts.js +1 -1
- package/src/foreignnodes.js +6 -1
- package/src/glbackend.js +1 -1
- package/src/glnodes.js +1 -1
- package/src/gloverlay.js +1 -1
- package/src/imagesource.js +4 -1
- package/src/node.d.ts +12 -0
- package/src/nodes/animation.js +1 -0
- package/src/nodes/boxpaint.js +5 -3
- package/src/nodes/image.js +3 -3
- package/src/nodes/invalidate.js +26 -0
- package/src/nodes/layout.js +4 -4
- package/src/nodes/layouthost.js +2 -0
- package/src/nodes/paint.js +1 -1
- package/src/nodes/queries.js +6 -1
- package/src/nodes/scrollable.js +5 -5
- package/src/nodes/text.js +1 -1
- package/src/nodes/window/capabilities.js +1 -1
- package/src/nodes/window/floors.js +90 -6
- package/src/nodes/window/flush.js +7 -15
- package/src/nodes/window/size.js +214 -10
- package/src/nodes/window/window.js +6 -0
- package/src/ntk.js +8 -31
- package/src/ntkroot.js +50 -0
- package/src/offscreen.js +33 -0
- package/src/paintcache.js +26 -15
- package/src/refresh/loader.js +23 -4
- package/src/screencolor.js +1 -1
- package/src/styles.js +1 -1
- package/src/svgnodes.js +1 -1
- package/src/symbols.js +4 -2
- package/src/trayhooks.js +1 -1
- package/src/wayland/context2d.js +2 -1
- package/src/wayland/decorations.js +1 -1
- package/src/win32/fonts.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-x11",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.19.0",
|
|
4
4
|
"description": "react renderer with X11 as a target",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -103,14 +103,14 @@
|
|
|
103
103
|
"dependencies": {
|
|
104
104
|
"ical.js": "^2.2.1",
|
|
105
105
|
"linebreak": "^1.1.0",
|
|
106
|
-
"ntk": "^8.
|
|
106
|
+
"ntk": "^8.10.0",
|
|
107
107
|
"react-reconciler": "^0.34.0",
|
|
108
108
|
"yoga-layout": "^3.2.1"
|
|
109
109
|
},
|
|
110
110
|
"optionalDependencies": {
|
|
111
111
|
"@windowkit/appkit": "^0.12.0",
|
|
112
112
|
"@windowkit/wayland": "^3.1.1",
|
|
113
|
-
"@windowkit/win32": "^0.0.
|
|
113
|
+
"@windowkit/win32": "^0.0.3",
|
|
114
114
|
"dbus-native": "^0.15.1",
|
|
115
115
|
"x11-dri": "^0.9.0"
|
|
116
116
|
},
|
package/src/Reconciler.js
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
import { createRequire } from 'node:module';
|
|
8
8
|
import React from 'react';
|
|
9
9
|
import ReactReconciler from 'react-reconciler';
|
|
10
|
-
import { createClient } from 'ntk';
|
|
11
10
|
|
|
12
11
|
import {
|
|
13
12
|
ConcurrentRoot,
|
|
@@ -22,6 +21,7 @@ import {
|
|
|
22
21
|
flushWindowMaps,
|
|
23
22
|
flushWindowRestacks,
|
|
24
23
|
} from './nodes/window/window.js';
|
|
24
|
+
import { loadNtk } from './ntkroot.js';
|
|
25
25
|
import { PopupNode } from './nodes/window/popup.js';
|
|
26
26
|
import { BoxNode } from './nodes/box.js';
|
|
27
27
|
import { THEME_SCOPE } from './nodes/kinds.js';
|
|
@@ -629,6 +629,8 @@ function loadIntegrations() {
|
|
|
629
629
|
* anyone hits and the least self-explanatory failure in the library.
|
|
630
630
|
*/
|
|
631
631
|
async function connect(options) {
|
|
632
|
+
// ntk's root is the X11 backend's, and loads with it (src/ntkroot.js)
|
|
633
|
+
const { createClient } = await loadNtk();
|
|
632
634
|
try {
|
|
633
635
|
return await createClient(options);
|
|
634
636
|
} catch (err) {
|
|
@@ -822,7 +824,12 @@ export async function createRoot(options = {}) {
|
|
|
822
824
|
const win32Asked =
|
|
823
825
|
rest.backend === 'win32' || process.env.REACT_X11_BACKEND === 'win32';
|
|
824
826
|
const connecting = !owned
|
|
825
|
-
?
|
|
827
|
+
? // an ntk app could only have come from ntk's own createClient: the
|
|
828
|
+
// root is imported already, and this hands it to the code that reads
|
|
829
|
+
// it synchronously (src/ntkroot.js)
|
|
830
|
+
isNtkApp(borrowed)
|
|
831
|
+
? loadNtk().then(() => borrowed)
|
|
832
|
+
: Promise.resolve(borrowed)
|
|
826
833
|
: backend === 'wayland'
|
|
827
834
|
? // Opt-in only, never through 'auto': X11 stays the default on Linux
|
|
828
835
|
// because the remote case (docs/remote.md) is the flagship reason
|
package/src/backend/context2d.js
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
// class keeps the JS-visible state (fillStyle strings, gradient objects,
|
|
24
24
|
// dash arrays) and re-syncs it when the backing surface is replaced after a
|
|
25
25
|
// resize — `_gen` is that generation.
|
|
26
|
-
import { cssColorStraight } from 'ntk';
|
|
26
|
+
import { cssColorStraight } from 'ntk/color';
|
|
27
27
|
|
|
28
28
|
const BLACK = [0, 0, 0, 1];
|
|
29
29
|
|
|
@@ -558,13 +558,50 @@ export class BackendContext2D {
|
|
|
558
558
|
|
|
559
559
|
setLineDash(segments) {
|
|
560
560
|
this._state.dash = Array.isArray(segments) ? segments : [];
|
|
561
|
-
|
|
561
|
+
// the offset is state of its own, and a new pattern keeps it
|
|
562
|
+
this._native.ctxSetLineDash(
|
|
563
|
+
this._s(),
|
|
564
|
+
this._state.dash,
|
|
565
|
+
this._state.dashOffset,
|
|
566
|
+
);
|
|
562
567
|
}
|
|
563
568
|
|
|
564
569
|
getLineDash() {
|
|
565
570
|
return [...this._state.dash];
|
|
566
571
|
}
|
|
567
572
|
|
|
573
|
+
/**
|
|
574
|
+
* Where along the pattern a stroke starts — the phase a marching dash
|
|
575
|
+
* moves. Missing here, a caller that asks `'lineDashOffset' in ctx` found
|
|
576
|
+
* no such thing and every stroke started its pattern at nought: the
|
|
577
|
+
* dashes of an animated edge were repainted sixteen times a second and
|
|
578
|
+
* never moved.
|
|
579
|
+
*/
|
|
580
|
+
/**
|
|
581
|
+
* Text drawn under a transform that scales is drawn scaled, glyphs and
|
|
582
|
+
* all: both bridges lay a text layout out once and draw its outlines
|
|
583
|
+
* through the context's matrix (Direct2D's `DrawTextLayout`, CoreText's
|
|
584
|
+
* `CTFrameDraw`). ntk's context on X11 cannot — its glyphs come out of a
|
|
585
|
+
* cache rasterized at the size they were shaped at, and a transform
|
|
586
|
+
* moves where each lands, not how big it is — and says nothing, which is
|
|
587
|
+
* the same answer. So an element that would rather draw a layout it has
|
|
588
|
+
* than shape a new one at every step of a zoom asks this first.
|
|
589
|
+
*/
|
|
590
|
+
get scalesText() {
|
|
591
|
+
return true;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
get lineDashOffset() {
|
|
595
|
+
return this._state.dashOffset;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
set lineDashOffset(value) {
|
|
599
|
+
// canvas ignores anything that is not a finite number
|
|
600
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) return;
|
|
601
|
+
this._state.dashOffset = value;
|
|
602
|
+
this._native.ctxSetLineDash(this._s(), this._state.dash, value);
|
|
603
|
+
}
|
|
604
|
+
|
|
568
605
|
save() {
|
|
569
606
|
// Sync before pushing: a fresh surface empties the stack on its way in,
|
|
570
607
|
// and a state pushed ahead of that sync was lost to it — the first
|
package/src/cocoa/app.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// (`_routeBatch`) with no pump at all (docs/macos.md §"JS on a worker").
|
|
17
17
|
import { isMainThread } from 'node:worker_threads';
|
|
18
18
|
|
|
19
|
-
import { cssColorStraight } from 'ntk';
|
|
19
|
+
import { cssColorStraight } from 'ntk/color';
|
|
20
20
|
|
|
21
21
|
import { deliverActivate, deliverOpen } from '../application.js';
|
|
22
22
|
import { flushPendingFrames } from '../frames.js';
|
package/src/cocoa/fonts.js
CHANGED
|
@@ -26,7 +26,8 @@ import { readFileSync } from 'node:fs';
|
|
|
26
26
|
import { inflateSync } from 'node:zlib';
|
|
27
27
|
|
|
28
28
|
import LineBreaker from 'linebreak';
|
|
29
|
-
import { cssColorStraight
|
|
29
|
+
import { cssColorStraight } from 'ntk/color';
|
|
30
|
+
import Font from 'ntk/font';
|
|
30
31
|
|
|
31
32
|
import { loadNative } from './native.js';
|
|
32
33
|
|
package/src/cocoa/presenter.js
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
// named repaints everything" rule the X11 damage model has), and geometry
|
|
25
25
|
// is re-diffed every frame because comparing four numbers is cheaper than
|
|
26
26
|
// knowing.
|
|
27
|
-
import { cssColorStraight } from 'ntk';
|
|
27
|
+
import { cssColorStraight } from 'ntk/color';
|
|
28
28
|
|
|
29
29
|
import { Node } from '../nodes/node.js';
|
|
30
30
|
import { addDamageRect, damageToPaint } from '../nodes/damage.js';
|
package/src/decorations.js
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
// derives is the padding a coverage surface has to carry. Both are pure
|
|
35
35
|
// functions — no connection, nothing drawn — so this module stays testable
|
|
36
36
|
// without a server.
|
|
37
|
-
import { shadowReach, shadowSigma } from 'ntk';
|
|
37
|
+
import { shadowReach, shadowSigma } from 'ntk/shadow-math';
|
|
38
38
|
|
|
39
39
|
/** A number, with or without the `px` CSS wants and this vocabulary does not. */
|
|
40
40
|
const LENGTH = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:px)?$/i;
|
package/src/fonts.js
CHANGED
package/src/foreignnodes.js
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
// save set, **never destroyed**. Destroying somebody else's window because a
|
|
31
31
|
// React tree changed is the failure mode this element is most able to cause,
|
|
32
32
|
// and `test/foreign.test.js` asserts against it first.
|
|
33
|
-
import {
|
|
33
|
+
import { x11Ntk } from './ntkroot.js';
|
|
34
34
|
|
|
35
35
|
import { isFocusable } from './a11y.js';
|
|
36
36
|
import { canEmbed } from './embedding.js';
|
|
@@ -161,6 +161,8 @@ export class ForeignNode extends Node {
|
|
|
161
161
|
if (!canEmbed(this.app)) return this._refuse();
|
|
162
162
|
const rect = this._geometry();
|
|
163
163
|
this.rect = rect;
|
|
164
|
+
// `<foreign>` embeds on an X connection only, where ntk's root is loaded
|
|
165
|
+
const { XEmbedSocket } = x11Ntk('<foreign>');
|
|
164
166
|
const socket = new XEmbedSocket(parent, {
|
|
165
167
|
...rect,
|
|
166
168
|
// react-x11 has a focus manager; a socket that answered
|
|
@@ -318,6 +320,8 @@ export class ForeignNode extends Node {
|
|
|
318
320
|
defaultFocus(info) {
|
|
319
321
|
const socket = this.socket;
|
|
320
322
|
if (!socket || !this.client) return;
|
|
323
|
+
// a socket is an X connection's, so ntk's root is loaded (src/ntkroot.js)
|
|
324
|
+
const { XEMBED } = x11Ntk('<foreign>');
|
|
321
325
|
const time = lastInputTime(this.app);
|
|
322
326
|
socket.activate(true, { time });
|
|
323
327
|
const detail =
|
|
@@ -338,6 +342,7 @@ export class ForeignNode extends Node {
|
|
|
338
342
|
if (!socket) return;
|
|
339
343
|
const time = lastInputTime(this.app);
|
|
340
344
|
if (this._focusSent) {
|
|
345
|
+
const { XEMBED } = x11Ntk('<foreign>');
|
|
341
346
|
socket.send(XEMBED.FOCUS_OUT, 0, 0, 0, { time });
|
|
342
347
|
this._focusSent = false;
|
|
343
348
|
}
|
package/src/glbackend.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
// `appcontext.js` (for `useSupports('shaders')`) and the node layer can use
|
|
20
20
|
// it without the two importing each other.
|
|
21
21
|
|
|
22
|
-
import { GLError } from 'ntk';
|
|
22
|
+
import { GLError } from 'ntk/gl';
|
|
23
23
|
|
|
24
24
|
/** Does this connection's policy ever choose the direct backend? */
|
|
25
25
|
const wantsDirect = (app) => {
|
package/src/glnodes.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// (NEXT_STEPS §4), sized and positioned by the parent's yoga layout like any
|
|
6
6
|
// other drawn node. Everything about the surface is here; the scene graph
|
|
7
7
|
// that draws into it comes later (docs/glx-plan.md).
|
|
8
|
-
import { cssColorStraight } from 'ntk';
|
|
8
|
+
import { cssColorStraight } from 'ntk/color';
|
|
9
9
|
|
|
10
10
|
// re-exported so the GL element layer stays one import for consumers
|
|
11
11
|
export { directGLFailure, hasDirectGL } from './glbackend.js';
|
package/src/gloverlay.js
CHANGED
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
// A leaf module, like src/embedding.js: `appcontext.js` asks `canOverlay`
|
|
61
61
|
// for `useSupports('glOverlay')`, and it imports nothing of ours but the
|
|
62
62
|
// damage model's arithmetic, which imports nothing at all.
|
|
63
|
-
import { cssColorStraight } from 'ntk';
|
|
63
|
+
import { cssColorStraight } from 'ntk/color';
|
|
64
64
|
|
|
65
65
|
import { FULL_DAMAGE, addDamageRect } from './nodes/damage.js';
|
|
66
66
|
import {
|
package/src/imagesource.js
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
// and the `cacheKey` cache live here, where a test needs no server; the node
|
|
7
7
|
// half in nodes/image.js is only lifecycle — when to resolve, when to claim
|
|
8
8
|
// damage, when to let go.
|
|
9
|
-
import { Image,
|
|
9
|
+
import { Image, decodeImage } from 'ntk/image';
|
|
10
|
+
|
|
11
|
+
import { x11Ntk } from './ntkroot.js';
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* A client-side object the 2d context composites as-is: an ntk `Image` or
|
|
@@ -429,6 +431,7 @@ export class DrawableSource {
|
|
|
429
431
|
picture() {
|
|
430
432
|
if (!this._picture) {
|
|
431
433
|
const Render = this.app.display.Render;
|
|
434
|
+
const { Picture } = x11Ntk('<image> on a server-side picture');
|
|
432
435
|
this._picture = new Picture(this.app, {
|
|
433
436
|
drawable: { id: this.id },
|
|
434
437
|
format: Render[DEPTH_FORMATS[this.depth]],
|
package/src/node.d.ts
CHANGED
|
@@ -100,6 +100,9 @@ export interface Context2D {
|
|
|
100
100
|
};
|
|
101
101
|
setLineDash(segments: number[]): void;
|
|
102
102
|
getLineDash(): number[];
|
|
103
|
+
/** Where along the dash pattern a stroke starts, in the same units as the
|
|
104
|
+
* pattern — the phase a marching dash moves. */
|
|
105
|
+
lineDashOffset: number;
|
|
103
106
|
beginPath(): void;
|
|
104
107
|
closePath(): void;
|
|
105
108
|
moveTo(x: number, y: number): void;
|
|
@@ -139,6 +142,15 @@ export interface Context2D {
|
|
|
139
142
|
strokeRect(x: number, y: number, width: number, height: number): void;
|
|
140
143
|
clearRect(x: number, y: number, width: number, height: number): void;
|
|
141
144
|
fillText(text: string, x: number, y: number): void;
|
|
145
|
+
/**
|
|
146
|
+
* `true` where text drawn under a transform that scales is drawn scaled,
|
|
147
|
+
* glyphs and all — the Windows and macOS contexts. Absent on X11, where
|
|
148
|
+
* ntk rasterizes glyphs at the size the text was shaped at and a
|
|
149
|
+
* transform moves only where each one lands. An element that would draw
|
|
150
|
+
* a layout it already has at another size, rather than shape a new one,
|
|
151
|
+
* asks this first.
|
|
152
|
+
*/
|
|
153
|
+
readonly scalesText?: boolean;
|
|
142
154
|
measureText(text: string): { width: number; [key: string]: unknown };
|
|
143
155
|
/**
|
|
144
156
|
* Composite an offscreen `Surface` (`react-x11/ntk`) — whole at a point,
|
package/src/nodes/animation.js
CHANGED
package/src/nodes/boxpaint.js
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
// `paintcache.js`: a named import of something an older ntk does not export
|
|
19
19
|
// is a *load-time* SyntaxError, which would take the renderer down rather
|
|
20
20
|
// than the one feature that needs it.
|
|
21
|
-
import
|
|
21
|
+
import { ntkRoot, x11Ntk } from '../ntkroot.js';
|
|
22
22
|
import { isFocusable as a11yFocusable } from '../a11y.js';
|
|
23
23
|
import { DEV } from './util.js';
|
|
24
24
|
|
|
@@ -320,7 +320,7 @@ export class NodeBoxPaint {
|
|
|
320
320
|
);
|
|
321
321
|
sctx.fill();
|
|
322
322
|
},
|
|
323
|
-
after: (surface) =>
|
|
323
|
+
after: (surface) => x11Ntk('a baked shadow').blurCoverage(surface, sigma),
|
|
324
324
|
live: () => this._paintShadowLive(ctx, plan),
|
|
325
325
|
};
|
|
326
326
|
const cache = this.root?._paintCache;
|
|
@@ -338,7 +338,9 @@ export class NodeBoxPaint {
|
|
|
338
338
|
* a k x k kernel, by the ratio of 2k to k squared.
|
|
339
339
|
*/
|
|
340
340
|
_paintShadowLive(ctx, plan) {
|
|
341
|
-
|
|
341
|
+
const ntk = ntkRoot();
|
|
342
|
+
if (typeof ntk?.Surface !== 'function' || !this.app?.display?.Render)
|
|
343
|
+
return;
|
|
342
344
|
let surface = null;
|
|
343
345
|
try {
|
|
344
346
|
surface = new ntk.Surface(this.app, {
|
package/src/nodes/image.js
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
// `paintcache.js`: a named import of something an older ntk does not export
|
|
21
21
|
// is a *load-time* SyntaxError, which would take the renderer down rather
|
|
22
22
|
// than the one feature that needs it.
|
|
23
|
-
import
|
|
23
|
+
import { loadImage } from 'ntk/image';
|
|
24
24
|
import { symbolWeight, symbolsFor, warnOnce } from '../symbols.js';
|
|
25
25
|
import { intrinsicSize } from './layout.js';
|
|
26
26
|
import { Node } from './node.js';
|
|
@@ -164,7 +164,7 @@ export class ImageNode extends Node {
|
|
|
164
164
|
_loadEntry(src) {
|
|
165
165
|
if (isPathImageSource(src)) {
|
|
166
166
|
return {
|
|
167
|
-
promise:
|
|
167
|
+
promise: loadImage(toLoadablePath(src)).then(
|
|
168
168
|
(image) => image,
|
|
169
169
|
(err) => {
|
|
170
170
|
console.error(
|
|
@@ -196,7 +196,7 @@ export class ImageNode extends Node {
|
|
|
196
196
|
async _loadFile(src) {
|
|
197
197
|
const token = ++this._loadToken;
|
|
198
198
|
try {
|
|
199
|
-
const image = await
|
|
199
|
+
const image = await loadImage(toLoadablePath(src));
|
|
200
200
|
if (token !== this._loadToken || this.destroyed) return;
|
|
201
201
|
this._ownedImage = image;
|
|
202
202
|
this._setImage(image);
|
package/src/nodes/invalidate.js
CHANGED
|
@@ -322,6 +322,11 @@ export class WindowInvalidate {
|
|
|
322
322
|
// panned — was a full measurement of the content floors a step.
|
|
323
323
|
if (reason !== 'scroll' && reason !== 'position') {
|
|
324
324
|
this._floorsDirty = true;
|
|
325
|
+
// …and which node changed, so that a change inside a box that sizes
|
|
326
|
+
// itself measures that box and nothing around it (`_floorsScope`).
|
|
327
|
+
// A claim that names no node, or names the window, is the tree's.
|
|
328
|
+
if (source && source !== this) this._floorsSources.add(source);
|
|
329
|
+
else this._floorsUnscoped = true;
|
|
325
330
|
}
|
|
326
331
|
if (reason !== 'scroll' && reason !== 'resize' && reason !== 'position') {
|
|
327
332
|
this._floorsContentDirty = true;
|
|
@@ -477,6 +482,27 @@ export class WindowInvalidate {
|
|
|
477
482
|
return WINDOW_ONLY;
|
|
478
483
|
}
|
|
479
484
|
|
|
485
|
+
/**
|
|
486
|
+
* One rect the layout diff says a node moved through (`layoutDiff.sink`,
|
|
487
|
+
* nodes/window/flush.js), to the lists that paint that node — the same
|
|
488
|
+
* reach a claim of it has (`_paneReach`). A node inside a surface is the
|
|
489
|
+
* panes' alone: the window's pass under the surface would repaint pixels
|
|
490
|
+
* no child of it ever had, and it went to both, so a card dragged across
|
|
491
|
+
* a graph drawn through GL was a window pass under the surface on every
|
|
492
|
+
* step. Answers whether the window's list took it.
|
|
493
|
+
*/
|
|
494
|
+
_claimLayoutMove(rect, node, cap) {
|
|
495
|
+
const reach =
|
|
496
|
+
this._overlaid.size !== 0 ? this._paneReach(node ?? null) : WINDOW_ONLY;
|
|
497
|
+
let windowTook = false;
|
|
498
|
+
if (reach !== PANES_ONLY && this._damage !== FULL_DAMAGE) {
|
|
499
|
+
this._damage = addDamageRect(this._damage, rect, cap);
|
|
500
|
+
windowTook = true;
|
|
501
|
+
}
|
|
502
|
+
if (reach !== WINDOW_ONLY) this._addPaneDamage(rect);
|
|
503
|
+
return windowTook;
|
|
504
|
+
}
|
|
505
|
+
|
|
480
506
|
/** One rect more for the panes, unless they repaint whole already. */
|
|
481
507
|
_addPaneDamage(rect) {
|
|
482
508
|
if (this._paneDamage === FULL_DAMAGE) return;
|
package/src/nodes/layout.js
CHANGED
|
@@ -313,17 +313,17 @@ export class NodeLayout {
|
|
|
313
313
|
) {
|
|
314
314
|
return;
|
|
315
315
|
}
|
|
316
|
-
if (had) layoutDiff.sink(insetRect(was, -grow));
|
|
316
|
+
if (had) layoutDiff.sink(insetRect(was, -grow), this);
|
|
317
317
|
if (width > 0 && height > 0) {
|
|
318
|
-
layoutDiff.sink(insetRect(this.abs, -grow));
|
|
318
|
+
layoutDiff.sink(insetRect(this.abs, -grow), this);
|
|
319
319
|
}
|
|
320
320
|
return;
|
|
321
321
|
}
|
|
322
322
|
if (had) {
|
|
323
|
-
layoutDiff.sink(insetRect(old, -grow));
|
|
323
|
+
layoutDiff.sink(insetRect(old, -grow), this);
|
|
324
324
|
}
|
|
325
325
|
if (width > 0 && height > 0) {
|
|
326
|
-
layoutDiff.sink(insetRect(this.abs, -grow));
|
|
326
|
+
layoutDiff.sink(insetRect(this.abs, -grow), this);
|
|
327
327
|
}
|
|
328
328
|
}
|
|
329
329
|
}
|
package/src/nodes/layouthost.js
CHANGED
|
@@ -415,6 +415,7 @@ export class NodeLayoutHost {
|
|
|
415
415
|
// content floors were measured from, as any style change is
|
|
416
416
|
root._floorsDirty = true;
|
|
417
417
|
root._floorsContentDirty = true;
|
|
418
|
+
root._floorsUnscoped = true;
|
|
418
419
|
}
|
|
419
420
|
}
|
|
420
421
|
|
|
@@ -944,6 +945,7 @@ export class WindowLayoutHost {
|
|
|
944
945
|
this._failedHosts.clear();
|
|
945
946
|
this._floorsDirty = true;
|
|
946
947
|
this._floorsContentDirty = true;
|
|
948
|
+
this._floorsUnscoped = true;
|
|
947
949
|
return true;
|
|
948
950
|
}
|
|
949
951
|
}
|
package/src/nodes/paint.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// window, background first.
|
|
4
4
|
|
|
5
5
|
import { isPlaced } from '../layouts.js';
|
|
6
|
-
import { Surface } from '../
|
|
6
|
+
import { Surface } from '../offscreen.js';
|
|
7
7
|
import { isPaintedColor } from './boxpaint.js';
|
|
8
8
|
import { DAMAGE_SLOP } from './damage.js';
|
|
9
9
|
import { DRAWN_KINDS } from './kinds.js';
|
package/src/nodes/queries.js
CHANGED
|
@@ -196,6 +196,7 @@ export class NodeQueries {
|
|
|
196
196
|
if (applyLayoutStyle(this.yoga, this.style, before) && this.root) {
|
|
197
197
|
this.root._floorsDirty = true;
|
|
198
198
|
this.root._floorsContentDirty = true;
|
|
199
|
+
this.root._floorsSources.add(this);
|
|
199
200
|
}
|
|
200
201
|
}
|
|
201
202
|
}
|
|
@@ -233,6 +234,7 @@ export class WindowQueries {
|
|
|
233
234
|
// a query block may carry layout properties, so the floors measured from
|
|
234
235
|
// the styles it is replacing are not the answer any more
|
|
235
236
|
this._floorsDirty = true;
|
|
237
|
+
this._floorsUnscoped = true;
|
|
236
238
|
for (const node of [...this._sizeQueryNodes]) {
|
|
237
239
|
if (node.destroyed) this._sizeQueryNodes.delete(node);
|
|
238
240
|
else node._sizeQueriesChanged();
|
|
@@ -324,7 +326,10 @@ export class WindowQueries {
|
|
|
324
326
|
// a block may carry layout properties, so the floors measured from the
|
|
325
327
|
// styles it is replacing are not the answer any more — the same debt a
|
|
326
328
|
// window query leaves (`_resolveSizeQueries`)
|
|
327
|
-
if (changed)
|
|
329
|
+
if (changed) {
|
|
330
|
+
this._floorsDirty = true;
|
|
331
|
+
this._floorsUnscoped = true;
|
|
332
|
+
}
|
|
328
333
|
return changed;
|
|
329
334
|
}
|
|
330
335
|
}
|
package/src/nodes/scrollable.js
CHANGED
|
@@ -301,11 +301,11 @@ export const Scrollable = (Base) =>
|
|
|
301
301
|
// for a `<glarea>` child's move that this pane rides (issue
|
|
302
302
|
// #644): its box claimed nothing, and a pane laid out again may
|
|
303
303
|
// have moved rows the move's copy carries where they were.
|
|
304
|
-
if (clamped || layoutDiff.ride) outer(vp);
|
|
304
|
+
if (clamped || layoutDiff.ride) outer(vp, this);
|
|
305
305
|
} else {
|
|
306
|
-
layoutDiff.sink = (rect) => {
|
|
306
|
+
layoutDiff.sink = (rect, node) => {
|
|
307
307
|
const clipped = intersectRects(rect, vp);
|
|
308
|
-
if (clipped) outer(clipped);
|
|
308
|
+
if (clipped) outer(clipped, node);
|
|
309
309
|
};
|
|
310
310
|
}
|
|
311
311
|
this._claimThumbs(thumbsWere, outer);
|
|
@@ -863,8 +863,8 @@ export const Scrollable = (Base) =>
|
|
|
863
863
|
if (sameThumb(before, after)) continue;
|
|
864
864
|
// a pixel of slop for the rounded corners' antialiasing, as the
|
|
865
865
|
// blit's own thumb repair takes
|
|
866
|
-
if (before) sink(insetRect(before, -1));
|
|
867
|
-
if (after) sink(insetRect(after, -1));
|
|
866
|
+
if (before) sink(insetRect(before, -1), this);
|
|
867
|
+
if (after) sink(insetRect(after, -1), this);
|
|
868
868
|
}
|
|
869
869
|
}
|
|
870
870
|
|
package/src/nodes/text.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// selection paints, and the glyph strip.
|
|
3
3
|
|
|
4
4
|
import { localTextStyleChanged, TEXT_REMEASURE } from '../styles.js';
|
|
5
|
-
import { cssColorStraight } from 'ntk';
|
|
5
|
+
import { cssColorStraight } from 'ntk/color';
|
|
6
6
|
import { hooks as a11yHooks } from '../a11y.js';
|
|
7
7
|
import { codePointAtOffset, codeUnitOffsets } from '../textrange.js';
|
|
8
8
|
import { NO_DAMAGE } from './damage.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// What a window can paint: compositing and ARGB transparency, the window
|
|
2
2
|
// background, and the capabilities `@supports` blocks read.
|
|
3
3
|
|
|
4
|
-
import { cssColorStraight } from 'ntk';
|
|
4
|
+
import { cssColorStraight } from 'ntk/color';
|
|
5
5
|
import {
|
|
6
6
|
argbVisual,
|
|
7
7
|
compositingActive,
|
|
@@ -197,6 +197,88 @@ export function collectFloorStale(node, stale, found, sweep) {
|
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
+
/**
|
|
201
|
+
* Whether `node` is a box its content cannot size and its surroundings cannot
|
|
202
|
+
* size either — an edge a floor measurement may stop at
|
|
203
|
+
* (`WindowNode._floorsScope`).
|
|
204
|
+
*
|
|
205
|
+
* Absolutely positioned, with a width and a height its style names as
|
|
206
|
+
* numbers: it is out of its parent's flow, so nothing measured inside it
|
|
207
|
+
* reaches anything above it (`contentSpan` skips it, and no floor is written
|
|
208
|
+
* on it), and every pass lays it out at those numbers wherever it sits, so
|
|
209
|
+
* what is inside it is measured the same in a pass over the window and in a
|
|
210
|
+
* pass over it alone. Nothing in its own box resolves against its parent —
|
|
211
|
+
* a percentage of padding would — and neither it nor its parent is a layout
|
|
212
|
+
* host, whose children are measured tree by tree.
|
|
213
|
+
*
|
|
214
|
+
* A card on a graph, a popover laid out inside the window, a sized overlay:
|
|
215
|
+
* the boxes a change inside is most often confined to. An in-flow box with a
|
|
216
|
+
* size of its own would do as well along its cross axis, but on its main
|
|
217
|
+
* axis a flex line can grow it past its width or shrink it under a minimum
|
|
218
|
+
* the author named, and that is a question about its siblings.
|
|
219
|
+
*/
|
|
220
|
+
export function isFloorBoundary(node) {
|
|
221
|
+
const style = node.style;
|
|
222
|
+
return (
|
|
223
|
+
node.yoga != null &&
|
|
224
|
+
!node.isWindow &&
|
|
225
|
+
style.position === 'absolute' &&
|
|
226
|
+
style.display !== 'none' &&
|
|
227
|
+
typeof style.width === 'number' &&
|
|
228
|
+
typeof style.height === 'number' &&
|
|
229
|
+
typeof style.minWidth !== 'string' &&
|
|
230
|
+
typeof style.minHeight !== 'string' &&
|
|
231
|
+
typeof style.maxWidth !== 'string' &&
|
|
232
|
+
typeof style.maxHeight !== 'string' &&
|
|
233
|
+
!PADDINGS.some((key) => typeof style[key] === 'string') &&
|
|
234
|
+
node._host === null &&
|
|
235
|
+
(node.parent?._host ?? null) === null
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const PADDINGS = [
|
|
240
|
+
'padding',
|
|
241
|
+
'paddingTop',
|
|
242
|
+
'paddingRight',
|
|
243
|
+
'paddingBottom',
|
|
244
|
+
'paddingLeft',
|
|
245
|
+
'paddingHorizontal',
|
|
246
|
+
'paddingVertical',
|
|
247
|
+
'paddingStart',
|
|
248
|
+
'paddingEnd',
|
|
249
|
+
];
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* The box that holds a change to `node` in (`isFloorBoundary`), or null when
|
|
253
|
+
* none does below `root`. Looked for from the parent up: a node that changed
|
|
254
|
+
* may have changed its own size, and it is what is *around* the change that
|
|
255
|
+
* has to hold still.
|
|
256
|
+
*/
|
|
257
|
+
export function floorBoundaryOf(node, root) {
|
|
258
|
+
for (let n = node.parent; n && n !== root; n = n.parent) {
|
|
259
|
+
if (n.isWindow) return null;
|
|
260
|
+
if (isFloorBoundary(n)) return n;
|
|
261
|
+
}
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Whether yoga has a change on record anywhere but inside `boundaries` and on
|
|
267
|
+
* the way down to them (`paths`) — something that changed with no node
|
|
268
|
+
* saying so, which a measurement confined to the boundaries would miss. A
|
|
269
|
+
* dirty node's ancestors are dirty too, so the walk goes down dirty nodes
|
|
270
|
+
* only, and a clean one has nothing under it to find.
|
|
271
|
+
*/
|
|
272
|
+
export function dirtyOutside(node, boundaries, paths) {
|
|
273
|
+
for (const child of node.children) {
|
|
274
|
+
if (!child.yoga || child.isWindow || !child.yoga.isDirty()) continue;
|
|
275
|
+
if (boundaries.has(child)) continue;
|
|
276
|
+
if (!paths.has(child)) return true;
|
|
277
|
+
if (dirtyOutside(child, boundaries, paths)) return true;
|
|
278
|
+
}
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
|
|
200
282
|
/**
|
|
201
283
|
* After a pass that settled the widths: which of the height floors, each a
|
|
202
284
|
* height *for a width*, were measured for a width their node no longer has.
|
|
@@ -216,7 +298,7 @@ export function collectFloorStale(node, stale, found, sweep) {
|
|
|
216
298
|
* the pixel grid off and the real one with it on, so the same layout reads
|
|
217
299
|
* a fraction apart between them.
|
|
218
300
|
*/
|
|
219
|
-
export function probeHeightFloors(node, root, hit) {
|
|
301
|
+
export function probeHeightFloors(node, root, hit, stop = root) {
|
|
220
302
|
for (const child of node.children) {
|
|
221
303
|
if (!child.yoga || child.isWindow) continue;
|
|
222
304
|
if (child.style.display === 'none') continue;
|
|
@@ -230,10 +312,10 @@ export function probeHeightFloors(node, root, hit) {
|
|
|
230
312
|
at !== undefined &&
|
|
231
313
|
child._heightForWidth(width) !== child._heightForWidth(at)
|
|
232
314
|
) {
|
|
233
|
-
markHeightStale(child, root, hit);
|
|
315
|
+
markHeightStale(child, root, hit, stop);
|
|
234
316
|
}
|
|
235
317
|
} else {
|
|
236
|
-
probeHeightFloors(child, root, hit);
|
|
318
|
+
probeHeightFloors(child, root, hit, stop);
|
|
237
319
|
}
|
|
238
320
|
// whatever the extent is, it is the extent for this width now — a
|
|
239
321
|
// stale one is about to be measured here, a clean one was just checked
|
|
@@ -241,10 +323,12 @@ export function probeHeightFloors(node, root, hit) {
|
|
|
241
323
|
}
|
|
242
324
|
}
|
|
243
325
|
|
|
244
|
-
/** A leaf whose height moved takes every extent above it with it
|
|
245
|
-
|
|
326
|
+
/** A leaf whose height moved takes every extent above it with it — up to
|
|
327
|
+
* `stop`, a box that sizes itself (`isFloorBoundary`), whose extent is its
|
|
328
|
+
* style's whatever moved inside it. */
|
|
329
|
+
function markHeightStale(node, root, hit, stop = root) {
|
|
246
330
|
hit.marked = true;
|
|
247
|
-
for (let n = node; n && n !==
|
|
331
|
+
for (let n = node; n && n !== stop; n = n.parent) {
|
|
248
332
|
if (n._floorH === undefined) continue;
|
|
249
333
|
n._floorH = undefined;
|
|
250
334
|
root._floorsStale.add(n);
|