react-x11 2.22.8 → 2.22.10
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/a11ypace.js +83 -0
- package/src/atspi.js +76 -18
- package/src/cocoa/app.js +30 -3
- package/src/cocoa/bezels.js +69 -1
- package/src/cocoa/window.js +24 -0
- package/src/components/Button.js +28 -12
- package/src/components/Select.js +3 -3
- package/src/components/native.js +43 -12
- package/src/frames.js +7 -0
- package/src/glnodes.js +26 -0
- package/src/gloverlay.js +772 -79
- package/src/nodes/paint.js +11 -2
- package/src/nodes/window/floors.js +40 -4
- package/src/win32/a11y.js +47 -39
package/src/nodes/paint.js
CHANGED
|
@@ -318,13 +318,22 @@ export class NodePaint {
|
|
|
318
318
|
return false;
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
-
|
|
321
|
+
/**
|
|
322
|
+
* The children, in paint order — or those up to and including `upTo`: an
|
|
323
|
+
* X11 pane over a `<glarea>` holds its own child and what is under it,
|
|
324
|
+
* never what is over it (src/gloverlay.js, `_paintPane`).
|
|
325
|
+
*/
|
|
326
|
+
_paintChildren(ctx, upTo = null) {
|
|
322
327
|
// A retained presenter replays a node's `paint` into a visual of that
|
|
323
328
|
// node's own — its children have visuals of their own, so it sets this
|
|
324
329
|
// for the duration of the call (src/cocoa/presenter.js, `paintSelf`).
|
|
325
330
|
// Never set on the X11 or surface paths, where a frame is one walk.
|
|
326
331
|
if (this._ownPaintOnly) return;
|
|
327
|
-
|
|
332
|
+
let order = this.paintOrder();
|
|
333
|
+
if (upTo !== null) {
|
|
334
|
+
const at = order.indexOf(upTo);
|
|
335
|
+
if (at !== -1) order = order.slice(0, at + 1);
|
|
336
|
+
}
|
|
328
337
|
if (order.length === 0) return;
|
|
329
338
|
if (!this.clipsChildren()) {
|
|
330
339
|
this._paintChildList(ctx, order);
|
|
@@ -104,8 +104,9 @@ export const mainAxisOf = (node) => {
|
|
|
104
104
|
/**
|
|
105
105
|
* Whether `child` is one the floors are **written on** along `axis`: a flex
|
|
106
106
|
* item on its container's main axis whose author left the minimum to the
|
|
107
|
-
* content
|
|
108
|
-
*
|
|
107
|
+
* content, and which flex could squeeze below it. The same test
|
|
108
|
+
* `writeFloors` applies, asked ahead of time — it is what decides whether a
|
|
109
|
+
* stale extent is one anybody will read.
|
|
109
110
|
*/
|
|
110
111
|
export function receivesFloor(child, axis) {
|
|
111
112
|
const parent = child.parent;
|
|
@@ -118,7 +119,37 @@ export function receivesFloor(child, axis) {
|
|
|
118
119
|
return false;
|
|
119
120
|
}
|
|
120
121
|
const own = axis === 'width' ? 'minWidth' : 'minHeight';
|
|
121
|
-
return typeof child.style[own] !== 'number';
|
|
122
|
+
return typeof child.style[own] !== 'number' && !floorIsNamedSize(child, axis);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Whether this item is its named size on `axis` in every pass, so that the
|
|
127
|
+
* floor a measurement would write is that same number and can never bind:
|
|
128
|
+
* a number for its size along the axis, as its flex basis (`flexBasis` unset
|
|
129
|
+
* or `'auto'`), `flexShrink: 0` and no `flexGrow`. Flex then takes its basis
|
|
130
|
+
* from that number and neither shrinks nor grows it, and measuring it is work
|
|
131
|
+
* nobody reads. A table's cells, at their columns' widths, are the case that
|
|
132
|
+
* paid: every row that scrolled into a `<Table>` was laid out alone for their
|
|
133
|
+
* floors before its frame could be, over half of a fling's frame time.
|
|
134
|
+
*
|
|
135
|
+
* A differential run over random trees, laid out with and without the
|
|
136
|
+
* rule, found why each clause is there. An auto basis is yoga's own
|
|
137
|
+
* measurement of the content, which can come out short of what the content
|
|
138
|
+
* needs, and the floor is what holds the item open. A named size that can
|
|
139
|
+
* grow is written as the size it grew to in the measuring pass, which the
|
|
140
|
+
* floor then keeps. And a `flexBasis` of its own is where the basis comes
|
|
141
|
+
* from instead of the size, and a percentage one is a different length in a
|
|
142
|
+
* measuring pass than in the real one.
|
|
143
|
+
*/
|
|
144
|
+
function floorIsNamedSize(child, axis) {
|
|
145
|
+
const style = child.style;
|
|
146
|
+
const basis = style.flexBasis;
|
|
147
|
+
return (
|
|
148
|
+
style.flexShrink === 0 &&
|
|
149
|
+
!(style.flexGrow > 0) &&
|
|
150
|
+
(basis === undefined || basis === 'auto') &&
|
|
151
|
+
typeof (axis === 'width' ? style.width : style.height) === 'number'
|
|
152
|
+
);
|
|
122
153
|
}
|
|
123
154
|
|
|
124
155
|
/**
|
|
@@ -848,7 +879,12 @@ export function writeFloors(node, axis) {
|
|
|
848
879
|
// A floor of 0 is what yoga does anyway, and an author who named their
|
|
849
880
|
// own `minWidth`/`minHeight` has already answered — overwriting it would
|
|
850
881
|
// put a measurement of ours above a number they wrote.
|
|
851
|
-
if (
|
|
882
|
+
if (
|
|
883
|
+
axisIsMain &&
|
|
884
|
+
inFlow(child) &&
|
|
885
|
+
typeof child.style[own] !== 'number' &&
|
|
886
|
+
!floorIsNamedSize(child, axis)
|
|
887
|
+
) {
|
|
852
888
|
const extent = horizontal ? child._floorW : child._floorH;
|
|
853
889
|
if (extent > 0) floor = extent;
|
|
854
890
|
}
|
package/src/win32/a11y.js
CHANGED
|
@@ -33,24 +33,21 @@ import {
|
|
|
33
33
|
} from '../a11y.js';
|
|
34
34
|
import { synthesizeClick } from '../events.js';
|
|
35
35
|
import { onApp } from '../trace-registry.js';
|
|
36
|
+
import { A11yPacer } from '../a11ypace.js';
|
|
36
37
|
|
|
37
38
|
const TRACE = process.env.REACT_X11_TRACE_A11Y === '1';
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
* sixty of them a second; a focus change, which it is waiting on, still
|
|
51
|
-
* pushes at once (`hooks.focus`), as does an announcement.
|
|
52
|
-
*/
|
|
53
|
-
const COMMIT_PUSH_MS = 100;
|
|
40
|
+
// How often commits may push the mirror is src/a11ypace.js's to say, for
|
|
41
|
+
// this bridge and the AT-SPI one alike: at once after a quiet spell, and for
|
|
42
|
+
// a stream of commits (a pan, a drag, a value ticking) once per interval,
|
|
43
|
+
// the last push catching up. UIA's clients are almost always listening on a
|
|
44
|
+
// desktop Windows (the touch keyboard, the text services and others ask for
|
|
45
|
+
// the tree), so pushing is not the rare case the listening gate makes it
|
|
46
|
+
// elsewhere. For a graph pane whose every item moves on every step of a pan,
|
|
47
|
+
// a push per commit was 1.6 ms of each frame. A screen reader reads positions
|
|
48
|
+
// on demand and does not need sixty of them a second. What it does wait on
|
|
49
|
+
// is pushed at once: a focus change (`hooks.focus`), an announcement, and a
|
|
50
|
+
// change to the focused element itself (`_touch`).
|
|
54
51
|
|
|
55
52
|
// --------------------------------------------------------------------------
|
|
56
53
|
// The one translation that is Windows' own
|
|
@@ -316,9 +313,12 @@ export class Win32Accessibility {
|
|
|
316
313
|
this._pushed = new Set();
|
|
317
314
|
this._unsubscribes = [];
|
|
318
315
|
this.dead = false;
|
|
319
|
-
/** When
|
|
320
|
-
this.
|
|
321
|
-
|
|
316
|
+
/** When commits may push (src/a11ypace.js). */
|
|
317
|
+
this._pacer = new A11yPacer();
|
|
318
|
+
/** The focused node, and whether a commit changed it: its own changes
|
|
319
|
+
* are what a screen reader is waiting to hear, and are not paced. */
|
|
320
|
+
this._focused = null;
|
|
321
|
+
this._urgentPending = false;
|
|
322
322
|
}
|
|
323
323
|
|
|
324
324
|
_idOf(node) {
|
|
@@ -454,23 +454,21 @@ export class Win32Accessibility {
|
|
|
454
454
|
}
|
|
455
455
|
}
|
|
456
456
|
|
|
457
|
-
/** A commit's push, at
|
|
457
|
+
/** A commit's push, paced (src/a11ypace.js), and at once where it
|
|
458
|
+
* changed the focused element. */
|
|
458
459
|
_commitFlush() {
|
|
459
460
|
if (this.dead || this._dirty.size === 0) return;
|
|
460
|
-
const
|
|
461
|
-
|
|
462
|
-
if (
|
|
463
|
-
|
|
464
|
-
this.
|
|
465
|
-
return;
|
|
461
|
+
const urgent = this._urgentPending;
|
|
462
|
+
this._urgentPending = false;
|
|
463
|
+
if (urgent) this._pacer.cancel();
|
|
464
|
+
if (urgent || this._pacer.ready(() => this._pacedFlush())) {
|
|
465
|
+
this._pacedFlush();
|
|
466
466
|
}
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
}, wait);
|
|
473
|
-
this._trailing.unref?.();
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
_pacedFlush() {
|
|
470
|
+
this._pacer.pushed();
|
|
471
|
+
this.flush();
|
|
474
472
|
}
|
|
475
473
|
|
|
476
474
|
/** A window's HWND exists now, so what is owed for it can be pushed. */
|
|
@@ -501,9 +499,19 @@ export class Win32Accessibility {
|
|
|
501
499
|
return root && this.toplevels.includes(root) ? root : null;
|
|
502
500
|
}
|
|
503
501
|
|
|
504
|
-
|
|
502
|
+
/** `own`: the change is the node's own (its props or text), which is
|
|
503
|
+
* urgent where the node is, or is inside, the focused element. */
|
|
504
|
+
_touch(node, own = false) {
|
|
505
505
|
const win = this._toplevelOf(node);
|
|
506
|
-
if (win)
|
|
506
|
+
if (!win) return;
|
|
507
|
+
this._dirty.add(win);
|
|
508
|
+
if (!own || this._focused === null || this._urgentPending) return;
|
|
509
|
+
for (let n = node; n; n = n.parent) {
|
|
510
|
+
if (n === this._focused) {
|
|
511
|
+
this._urgentPending = true;
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
507
515
|
}
|
|
508
516
|
|
|
509
517
|
// ---- what the shell asks for ------------------------------------------
|
|
@@ -594,10 +602,11 @@ export class Win32Accessibility {
|
|
|
594
602
|
};
|
|
595
603
|
hooks.attached = (parent) => this._touch(parent);
|
|
596
604
|
hooks.detach = (parent) => this._touch(parent);
|
|
597
|
-
hooks.propsChanged = (node) => this._touch(node);
|
|
598
|
-
hooks.textContent = (chunk) => this._touch(chunk.parent ?? chunk);
|
|
599
|
-
hooks.textState = (node) => this._touch(node);
|
|
605
|
+
hooks.propsChanged = (node) => this._touch(node, true);
|
|
606
|
+
hooks.textContent = (chunk) => this._touch(chunk.parent ?? chunk, true);
|
|
607
|
+
hooks.textState = (node) => this._touch(node, true);
|
|
600
608
|
hooks.focus = (previous, next) => {
|
|
609
|
+
this._focused = next ?? null;
|
|
601
610
|
const win = this._toplevelOf(next ?? previous);
|
|
602
611
|
if (!win) return;
|
|
603
612
|
this._dirty.add(win);
|
|
@@ -637,8 +646,7 @@ export class Win32Accessibility {
|
|
|
637
646
|
bury() {
|
|
638
647
|
if (this.dead) return;
|
|
639
648
|
this.dead = true;
|
|
640
|
-
|
|
641
|
-
this._trailing = null;
|
|
649
|
+
this._pacer.cancel();
|
|
642
650
|
for (const key of Object.keys(hooks)) hooks[key] = null;
|
|
643
651
|
for (const unsubscribe of this._unsubscribes) unsubscribe();
|
|
644
652
|
this._unsubscribes = [];
|