react-x11 2.17.1 → 2.18.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-x11",
3
- "version": "2.17.1",
3
+ "version": "2.18.1",
4
4
  "description": "react renderer with X11 as a target",
5
5
  "main": "./src/index.js",
6
6
  "files": [
@@ -28,6 +28,8 @@
28
28
  // so the ladder is visible before it is built. Policy decides, the machine
29
29
  // answers — the same rule the GL policy follows everywhere else.
30
30
 
31
+ import { withoutActions } from './quiet.js';
32
+
31
33
  const RUNGS = ['auto', 'gl'];
32
34
  const KNOWN_RUNGS = ['auto', 'gl', 'gles', 'webgpu'];
33
35
 
@@ -145,8 +147,11 @@ export class CocoaGLArea {
145
147
  this.scale = this.parent.scale ?? app.scale ?? 1;
146
148
  this.destroyed = false;
147
149
  this._reactX11Node = null;
148
- this.layer = this._native.createLayer();
149
- this._native.addSublayer(this.parent._layer, this.layer);
150
+ this.layer = withoutActions(this._native, () => {
151
+ const layer = this._native.createLayer();
152
+ this._native.addSublayer(this.parent._layer, layer);
153
+ return layer;
154
+ });
150
155
  this.rect = null;
151
156
  this.setState({
152
157
  x: options.x ?? 0,
@@ -254,7 +259,9 @@ export class CocoaGLArea {
254
259
  this.destroyed = true;
255
260
  this._context?._destroy();
256
261
  this._context = null;
257
- this._native.removeFromSuperlayer(this.layer);
262
+ withoutActions(this._native, () =>
263
+ this._native.removeFromSuperlayer(this.layer),
264
+ );
258
265
  }
259
266
  }
260
267
 
@@ -15,6 +15,7 @@
15
15
  // was painted on the layer: ntk blits an X window's backing store on its
16
16
  // own, where a layer's contents are a copy the bitmap has to be pushed to.
17
17
  import { BackendContext2D } from '../backend/context2d.js';
18
+ import { withoutActions } from './quiet.js';
18
19
 
19
20
  export const OVERLAY_Z = 1e7 + 1;
20
21
 
@@ -25,8 +26,11 @@ export class CocoaOverlayPane {
25
26
  this._native = app._native;
26
27
  this.scale = this.parent.scale ?? app.scale ?? 1;
27
28
  this.destroyed = false;
28
- this.layer = this._native.createLayer();
29
- this._native.addSublayer(this.parent._layer, this.layer);
29
+ this.layer = withoutActions(this._native, () => {
30
+ const layer = this._native.createLayer();
31
+ this._native.addSublayer(this.parent._layer, layer);
32
+ return layer;
33
+ });
30
34
  this.rect = null;
31
35
  this._surface = null;
32
36
  this._surfaceSize = null;
@@ -154,6 +158,11 @@ export class CocoaOverlayPane {
154
158
  this.destroyed = true;
155
159
  this._release();
156
160
  this._ctx = null;
157
- this._native.removeFromSuperlayer(this.layer);
161
+ // Actions off, or the layer fades out over a quarter of a second, what
162
+ // the children last painted going translucent over a surface still
163
+ // drawing (#638).
164
+ withoutActions(this._native, () =>
165
+ this._native.removeFromSuperlayer(this.layer),
166
+ );
158
167
  }
159
168
  }
@@ -3,13 +3,18 @@
3
3
  // contents are whatever IOSurface the pane process last presented. The pane
4
4
  // owns its buffers and its drawing; this side owns the layer, the layout
5
5
  // and the input — CPU offloading, not isolation (docs/frame.md).
6
+ import { withoutActions } from './quiet.js';
7
+
6
8
  export class CocoaPaneHost {
7
9
  constructor(app, wnd) {
8
10
  this.app = app;
9
11
  this.wnd = wnd;
10
12
  this._native = app._native;
11
- this.layer = this._native.createLayer();
12
- this._native.addSublayer(wnd._layer, this.layer);
13
+ this.layer = withoutActions(this._native, () => {
14
+ const layer = this._native.createLayer();
15
+ this._native.addSublayer(wnd._layer, layer);
16
+ return layer;
17
+ });
13
18
  this.destroyed = false;
14
19
  this._rect = null;
15
20
  }
@@ -73,6 +78,8 @@ export class CocoaPaneHost {
73
78
  destroy() {
74
79
  if (this.destroyed) return;
75
80
  this.destroyed = true;
76
- this._native.removeFromSuperlayer(this.layer);
81
+ withoutActions(this._native, () =>
82
+ this._native.removeFromSuperlayer(this.layer),
83
+ );
77
84
  }
78
85
  }
@@ -0,0 +1,13 @@
1
+ // Layer changes with Core Animation's implicit actions off, for a layer
2
+ // that is ours rather than a presenter's (a <glarea>'s surface and overlay,
3
+ // a frame pane's host): no frame's transaction covers it, so a bare
4
+ // `addSublayer` or `removeFromSuperlayer` takes the default quarter-second
5
+ // order-in or order-out fade, and a bare property set tweens.
6
+ export function withoutActions(native, fn) {
7
+ native.txBegin({ disableActions: true });
8
+ try {
9
+ return fn();
10
+ } finally {
11
+ native.txCommit();
12
+ }
13
+ }
package/src/events.js CHANGED
@@ -1270,7 +1270,9 @@ export class EventManager {
1270
1270
  // arrive
1271
1271
  if (!rect?.width || !rect.height) continue;
1272
1272
  if (node.hidden || node.style.display === 'none') continue;
1273
- if (node.style.pointerEvents === 'none') continue;
1273
+ // neither value lets the pointer arrive at the node itself
1274
+ const pe = node.style.pointerEvents;
1275
+ if (pe === 'none' || pe === 'box-none') continue;
1274
1276
  const eta =
1275
1277
  speed < ATTENTION_MIN_SPEED
1276
1278
  ? attentionEta(native.x, native.y, 0, 0, rect)
package/src/glnodes.js CHANGED
@@ -418,8 +418,9 @@ export class GlAreaNode extends Node {
418
418
  const { width, height } = this.rect;
419
419
  // x/y are where the node's origin sits in the drawable being drawn
420
420
  // into (DrawInfo's contract) — a <glarea> draws into its own X window,
421
- // so that is the origin.
422
- const info = { width, height, x: 0, y: 0, node: this };
421
+ // so that is the origin. width/height are device pixels; `scale` takes
422
+ // them back to logical ones.
423
+ const info = { width, height, x: 0, y: 0, scale: this.scale, node: this };
423
424
  if (!this._created) {
424
425
  this._created = true;
425
426
  this.props.onCreated?.(gl, info);
@@ -481,7 +482,9 @@ export class GlAreaNode extends Node {
481
482
  * by those. With no GL surface — not made yet, or given up after
482
483
  * `onError` — only the children answer, and a point between them is the
483
484
  * tree's. Hidden, or `pointerEvents: 'none'` here or above, lets the
484
- * pointer through to what the tree has behind, as it does for any node.
485
+ * pointer through to what the tree has behind, as it does for any node;
486
+ * `'box-none'` here does the same for the surface alone and still lets
487
+ * its children take the pointer.
485
488
  */
486
489
  hitSurface(x, y) {
487
490
  const panes = this._overlay?.panes.length ?? 0;
@@ -510,6 +513,10 @@ export class GlAreaNode extends Node {
510
513
  if (hit) return hit;
511
514
  }
512
515
  }
516
+ // `box-none`: the children above answered for themselves, and the
517
+ // surface between them is not a target — the point goes on to the tree
518
+ // behind, as it does for any node with that value
519
+ if (this.style?.pointerEvents === 'box-none') return null;
513
520
  return this.window ? this : null;
514
521
  }
515
522
 
@@ -170,16 +170,19 @@ export class NodeHitTest {
170
170
  return null;
171
171
  }
172
172
  const inside = this.containsPoint(x, y);
173
+ // `box-none`: the children are targets and this node is not — a point
174
+ // over its own area goes to whatever is behind it
175
+ const self = this.style.pointerEvents !== 'box-none';
173
176
  // the children are culled on the strict rect — slop grows this node's
174
177
  // target, not the region its clip lets through
175
178
  if (!inside && this.clipsChildren()) {
176
- return this.containsPointWithSlop(x, y) ? this : null;
179
+ return self && this.containsPointWithSlop(x, y) ? this : null;
177
180
  }
178
181
  const order = this.paintOrder();
179
182
  for (let i = order.length - 1; i >= 0; i--) {
180
183
  const hit = order[i].hitTest(x, y);
181
184
  if (hit) return hit;
182
185
  }
183
- return inside || this.containsPointWithSlop(x, y) ? this : null;
186
+ return self && (inside || this.containsPointWithSlop(x, y)) ? this : null;
184
187
  }
185
188
  }
@@ -72,7 +72,14 @@ export type JustifyItems = 'flex-start' | 'center' | 'flex-end' | 'stretch';
72
72
  export type JustifySelf = 'auto' | JustifyItems;
73
73
  export type Overflow = 'visible' | 'hidden' | 'scroll';
74
74
  export type BorderStyle = 'solid' | 'dashed';
75
- export type PointerEvents = 'auto' | 'none';
75
+ /**
76
+ * Whether the pointer can land on a node. `'none'`: not on it nor anything
77
+ * inside it. `'box-none'` (React Native's): not on the node itself, but on
78
+ * its children — a press on the node's own area goes through to what is
79
+ * behind it. The shape an overlay wants when it holds controls but must not
80
+ * swallow the clicks between them, `<glarea>` included.
81
+ */
82
+ export type PointerEvents = 'auto' | 'none' | 'box-none';
76
83
  export type TextAlign = 'left' | 'right' | 'center' | 'start' | 'end';
77
84
  export type FontStyle = 'normal' | 'italic' | 'oblique';
78
85
  export type FontWeight = number | 'normal' | 'bold';