react-x11 2.47.4 → 2.48.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/app.js +21 -0
- package/src/cocoa/dnd.js +29 -4
- package/src/cocoa/panehost.js +24 -5
- package/src/cocoa/window.js +3 -1
- package/src/index.d.ts +6 -0
- package/src/nodes/window/floors.js +26 -1
- package/src/nodes/window/spine.js +21 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-x11",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.48.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.29.0",
|
|
113
113
|
"@windowkit/wayland": "^3.1.1",
|
|
114
114
|
"@windowkit/win32": "^0.0.9",
|
|
115
115
|
"dbus-native": "^0.16.0",
|
package/src/cocoa/app.js
CHANGED
|
@@ -71,6 +71,9 @@ const PUMP_INTERVAL_MS = 8;
|
|
|
71
71
|
// to 100, docs/windows.md §"Resize"). `createRoot({ cocoa: { resizeWait } })`
|
|
72
72
|
// sets it; 0 is no handshake at all.
|
|
73
73
|
const RESIZE_WAIT_MS = 50;
|
|
74
|
+
// What `createRoot({ cocoa: { paneStretch } })` and
|
|
75
|
+
// REACT_X11_COCOA_PANE_STRETCH take (src/cocoa/panehost.js, `_stretch`).
|
|
76
|
+
const PANE_STRETCH = ['behind', 'never', 'always'];
|
|
74
77
|
// How often the screen layout is re-read while something is watching it, in
|
|
75
78
|
// ms. There is no event to wait for — the bridge keeps its `NSScreen` copy
|
|
76
79
|
// current on macOS's notification and emits nothing (#617) — so
|
|
@@ -149,6 +152,24 @@ export class CocoaApp {
|
|
|
149
152
|
);
|
|
150
153
|
}
|
|
151
154
|
this._resizeWait = resizeWait;
|
|
155
|
+
// How a `<Frame>` pane's last frame is shown while the frame of the
|
|
156
|
+
// size its layer was just given is on its way (src/cocoa/panehost.js,
|
|
157
|
+
// `_stretch`): 'behind' stretches it across once the pane falls behind
|
|
158
|
+
// `resizeWait`, 'never' keeps it at its size however late the pane is,
|
|
159
|
+
// and 'always' stretches it both ways. Apart from `resizeWait`, which
|
|
160
|
+
// is also how long AppKit holds the window's edge for a frame, so a
|
|
161
|
+
// pane that is never to be stretched would otherwise need a budget
|
|
162
|
+
// that held the drag back as long.
|
|
163
|
+
const paneStretch =
|
|
164
|
+
options.cocoa?.paneStretch ??
|
|
165
|
+
(process.env.REACT_X11_COCOA_PANE_STRETCH || 'behind');
|
|
166
|
+
if (!PANE_STRETCH.includes(paneStretch)) {
|
|
167
|
+
throw new TypeError(
|
|
168
|
+
'react-x11: cocoa.paneStretch (REACT_X11_COCOA_PANE_STRETCH) is ' +
|
|
169
|
+
`one of ${PANE_STRETCH.join(', ')} — got ${paneStretch}.`,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
this._paneStretch = paneStretch;
|
|
152
173
|
|
|
153
174
|
// The activation policy has to be fixed before the app finishes
|
|
154
175
|
// launching — a Regular launch registers a Dock tile, so an agent app
|
package/src/cocoa/dnd.js
CHANGED
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
// the pasteboard, so an in-app drop still gets `e.items` by reference and
|
|
35
35
|
// `e.source === 'internal'`, the contract the X11 transport keeps. Other
|
|
36
36
|
// applications read the pasteboard, where a thunk is a promise the bridge
|
|
37
|
-
// asks `provide` to keep.
|
|
37
|
+
// asks `provide` to keep — on the main thread. On a worker the bridge takes
|
|
38
|
+
// no `provide`, and a thunk resolves as the drag begins (`dragSpec`).
|
|
38
39
|
//
|
|
39
40
|
// ## Types
|
|
40
41
|
//
|
|
@@ -223,6 +224,16 @@ export const wire = (value) =>
|
|
|
223
224
|
* to keep. The files thunk alone resolves now, because the items cannot
|
|
224
225
|
* be counted without it.
|
|
225
226
|
*
|
|
227
|
+
* Unless the app is on a worker (`upFront`, threaded mode): AppKit asks a
|
|
228
|
+
* provider from inside its own callback on the main thread, which a worker
|
|
229
|
+
* cannot answer in time, so the bridge refuses `provide` there with a
|
|
230
|
+
* TypeError. That was every drag on a Mac, since an app runs on a worker
|
|
231
|
+
* by default. There every thunk resolves now, as the win32 backend has
|
|
232
|
+
* them, and this is the external promotion `_resolve` caches for: an
|
|
233
|
+
* in-app drop is handed the same value by reference. A value that resolves
|
|
234
|
+
* to nothing, or that JSON cannot carry (a cycle, a node), is left off
|
|
235
|
+
* the pasteboard rather than failing the drag; an in-app drop still has it.
|
|
236
|
+
*
|
|
226
237
|
* No image, and so no slide-back. The picture of the drag is the app's own
|
|
227
238
|
* `<popup dragPreview>`; what the system session carries is a blank 1×1.
|
|
228
239
|
* Left at AppKit's default, a refused or cancelled drop plays
|
|
@@ -236,7 +247,7 @@ export const wire = (value) =>
|
|
|
236
247
|
* to animate from `onDragEnd`. Not a seam: there is no system image for
|
|
237
248
|
* a caller to want slid back.
|
|
238
249
|
*/
|
|
239
|
-
export function dragSpec(session, native, scale) {
|
|
250
|
+
export function dragSpec(session, native, scale, { upFront = false } = {}) {
|
|
240
251
|
const types = session.types;
|
|
241
252
|
const reverse = new Map(); // uti -> react-x11 type
|
|
242
253
|
const items = [];
|
|
@@ -256,7 +267,17 @@ export function dragSpec(session, native, scale) {
|
|
|
256
267
|
if (!uti || uti in first) continue;
|
|
257
268
|
reverse.set(uti, type);
|
|
258
269
|
const raw = session._data[type];
|
|
259
|
-
|
|
270
|
+
if (!upFront) {
|
|
271
|
+
first[uti] = typeof raw === 'function' ? null : wire(raw);
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
const value = session._resolve(type);
|
|
275
|
+
if (value == null) continue;
|
|
276
|
+
try {
|
|
277
|
+
first[uti] = wire(value);
|
|
278
|
+
} catch {
|
|
279
|
+
// not something the pasteboard can carry; the in-app drop has it
|
|
280
|
+
}
|
|
260
281
|
}
|
|
261
282
|
if (Object.keys(first).length) {
|
|
262
283
|
if (items.length) Object.assign(items[0], first);
|
|
@@ -269,7 +290,11 @@ export function dragSpec(session, native, scale) {
|
|
|
269
290
|
items,
|
|
270
291
|
operations: session.actions,
|
|
271
292
|
slideBack: false,
|
|
272
|
-
|
|
293
|
+
...(upFront
|
|
294
|
+
? null
|
|
295
|
+
: {
|
|
296
|
+
provide: (uti) => wire(session._resolve(reverse.get(uti) ?? uti)),
|
|
297
|
+
}),
|
|
273
298
|
};
|
|
274
299
|
}
|
|
275
300
|
|
package/src/cocoa/panehost.js
CHANGED
|
@@ -91,7 +91,8 @@ class PaneLayer {
|
|
|
91
91
|
* and a smeared strip. The budget is the window's live-resize
|
|
92
92
|
* handshake's (`createRoot({ cocoa: { resizeWait } })`), how long a frame
|
|
93
93
|
* of a new size may take before the old one is shown in its place: 0
|
|
94
|
-
* stretches always, both ways, as before.
|
|
94
|
+
* stretches always, both ways, as before. And the app may say otherwise
|
|
95
|
+
* whatever the budget (`_stretch`).
|
|
95
96
|
*
|
|
96
97
|
* Down the layer the frame is at its size whatever this says. A width
|
|
97
98
|
* moves where a page's lines break, and what comes is a page laid out
|
|
@@ -116,8 +117,9 @@ class PaneLayer {
|
|
|
116
117
|
* decides the rest (`_answered`).
|
|
117
118
|
*/
|
|
118
119
|
_anchoredNow() {
|
|
120
|
+
const stretch = this._stretch();
|
|
121
|
+
if (stretch !== 'behind') return stretch === 'never';
|
|
119
122
|
const budget = this.app._resizeWait ?? RESIZE_WAIT_MS;
|
|
120
|
-
if (!(budget > 0)) return false;
|
|
121
123
|
// a size asked longer ago than the budget, and no frame of any since
|
|
122
124
|
const oldest = this._asked[0];
|
|
123
125
|
if (oldest && now() - Math.max(oldest.at, this._answeredAt) > budget) {
|
|
@@ -126,6 +128,23 @@ class PaneLayer {
|
|
|
126
128
|
return !this._behind;
|
|
127
129
|
}
|
|
128
130
|
|
|
131
|
+
/**
|
|
132
|
+
* When the frame the layer holds is stretched to bounds that are not its
|
|
133
|
+
* size: `'behind'`, across, once the pane falls behind the budget
|
|
134
|
+
* (`_anchoredNow`); `'never'`, kept at its size and its edge carried over
|
|
135
|
+
* however late the pane is; `'always'`, both ways, Core Animation's own.
|
|
136
|
+
* The app's `cocoa.paneStretch` (REACT_X11_COCOA_PANE_STRETCH), and
|
|
137
|
+
* `'behind'` where it names none, or `'always'` at a budget of 0, which
|
|
138
|
+
* waits for no frame. An app that says `'never'` was not served by the
|
|
139
|
+
* budget: as long as AppKit holds the window's edge for a frame, so a
|
|
140
|
+
* budget a slow page never misses held every step of the drag back.
|
|
141
|
+
*/
|
|
142
|
+
_stretch() {
|
|
143
|
+
const stretch = this.app._paneStretch ?? 'behind';
|
|
144
|
+
if (stretch !== 'behind') return stretch;
|
|
145
|
+
return (this.app._resizeWait ?? RESIZE_WAIT_MS) > 0 ? 'behind' : 'always';
|
|
146
|
+
}
|
|
147
|
+
|
|
129
148
|
/**
|
|
130
149
|
* The props that show the frame the layer holds in bounds that are not
|
|
131
150
|
* its size, where they changed. At its size and from the top left, as a
|
|
@@ -138,7 +157,8 @@ class PaneLayer {
|
|
|
138
157
|
* the frame is stretched, and down it is still at its size
|
|
139
158
|
* (`_anchoredNow`): the whole of each row stretched to the new width,
|
|
140
159
|
* cropped or with its last row carried down. Both rects are the whole
|
|
141
|
-
* when the frame is the layer's size, and
|
|
160
|
+
* when the frame is the layer's size, and where it is stretched always
|
|
161
|
+
* (`_stretch`).
|
|
142
162
|
*
|
|
143
163
|
* Anchored by gravity alone, the strip the frame did not cover showed
|
|
144
164
|
* whatever was under the pane, which is the `<Frame>`'s background: in
|
|
@@ -159,8 +179,7 @@ class PaneLayer {
|
|
|
159
179
|
const rect = this._rect;
|
|
160
180
|
let crop = null;
|
|
161
181
|
let edge = null;
|
|
162
|
-
|
|
163
|
-
if (frame && rect && budget > 0) {
|
|
182
|
+
if (frame && rect && this._stretch() !== 'always') {
|
|
164
183
|
// across at its size while the pane keeps up, and down always; an
|
|
165
184
|
// axis stretched is shown whole, the frame's every column across
|
|
166
185
|
const across = this._anchoredNow();
|
package/src/cocoa/window.js
CHANGED
|
@@ -520,7 +520,9 @@ export class CocoaWindow {
|
|
|
520
520
|
if (this.destroyed) return null;
|
|
521
521
|
const began = this._native.beginDrag(
|
|
522
522
|
this._h,
|
|
523
|
-
dragSpec(session, this._native, this.scale
|
|
523
|
+
dragSpec(session, this._native, this.scale, {
|
|
524
|
+
upFront: this.app._threaded,
|
|
525
|
+
}),
|
|
524
526
|
);
|
|
525
527
|
this.app._afterInput();
|
|
526
528
|
return began;
|
package/src/index.d.ts
CHANGED
|
@@ -306,6 +306,11 @@ export interface RootOptions {
|
|
|
306
306
|
* the next comes, and past it, it is stretched across to the pane's new
|
|
307
307
|
* width and stays at its size down it; at 0 every one is stretched both
|
|
308
308
|
* ways.
|
|
309
|
+
* `paneStretch` says when a `<Frame>` pane's last frame is stretched
|
|
310
|
+
* instead, whatever `resizeWait` is: `'behind'` (the default) as above,
|
|
311
|
+
* `'never'` keeps it at its size however late the pane's next frame is,
|
|
312
|
+
* and `'always'` stretches it both ways. `REACT_X11_COCOA_PANE_STRETCH`
|
|
313
|
+
* says the same from the environment.
|
|
309
314
|
* `screenPoll` is how often the screen layout is re-read while a
|
|
310
315
|
* {@link useScreens} subscriber is mounted, in ms — 500 by default, 0 for
|
|
311
316
|
* never. macOS has no event for a display plugged in, unplugged or
|
|
@@ -333,6 +338,7 @@ export interface RootOptions {
|
|
|
333
338
|
frameInterval?: number;
|
|
334
339
|
pumpInterval?: number;
|
|
335
340
|
resizeWait?: number;
|
|
341
|
+
paneStretch?: 'behind' | 'never' | 'always';
|
|
336
342
|
screenPoll?: number;
|
|
337
343
|
appName?: string;
|
|
338
344
|
activationPolicy?: 'regular' | 'accessory' | 'prohibited';
|
|
@@ -333,7 +333,8 @@ export function dirtyOutside(node, boundaries, paths) {
|
|
|
333
333
|
* Undefined where it cannot be summed: a child with no extent on hand, a
|
|
334
334
|
* negative margin (the boxes would overlap, and the last would not have to
|
|
335
335
|
* be the lowest), or nothing in flow at all — a leaf's answer, which is
|
|
336
|
-
* `contentSpan`'s to give.
|
|
336
|
+
* `contentSpan`'s to give. All but the first are the column's own, and a
|
|
337
|
+
* spine never sums a column that has them (`columnSums`).
|
|
337
338
|
*/
|
|
338
339
|
export function columnHeightSpan(node) {
|
|
339
340
|
const yoga = node.yoga;
|
|
@@ -363,6 +364,30 @@ export function columnHeightSpan(node) {
|
|
|
363
364
|
);
|
|
364
365
|
}
|
|
365
366
|
|
|
367
|
+
/**
|
|
368
|
+
* Whether `columnHeightSpan` has an answer for this column once its children
|
|
369
|
+
* carry extents: everything else it asks — something in flow, margins down
|
|
370
|
+
* that are lengths and pull no box over the one before it, a gap that is a
|
|
371
|
+
* length — asked ahead of the measurement, from the same readings.
|
|
372
|
+
*
|
|
373
|
+
* A spine asks it before it sums a column (`isSummableColumn`), because once
|
|
374
|
+
* the measurement is under way a sum with no answer has nowhere to go: it is
|
|
375
|
+
* written as the column's extent, an extent of none writes the style's own
|
|
376
|
+
* minimum, and the column — and every column summed above it — lost its
|
|
377
|
+
* floor to a label that changed beside a block with `marginTop: -4`.
|
|
378
|
+
*/
|
|
379
|
+
export function columnSums(node) {
|
|
380
|
+
if (columnGap(node) === undefined) return false;
|
|
381
|
+
let count = 0;
|
|
382
|
+
for (const child of node.children) {
|
|
383
|
+
if (!inFlow(child)) continue;
|
|
384
|
+
const { top, bottom } = marginsDown(child);
|
|
385
|
+
if (!(top >= 0 && bottom >= 0)) return false;
|
|
386
|
+
count += 1;
|
|
387
|
+
}
|
|
388
|
+
return count !== 0;
|
|
389
|
+
}
|
|
390
|
+
|
|
366
391
|
/**
|
|
367
392
|
* `marginDown` for both edges of a box, kept on the box with the style object
|
|
368
393
|
* it was read under. A margin reaches yoga only from the style
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
// length — is not a spine, and the tree is measured whole.
|
|
37
37
|
|
|
38
38
|
import {
|
|
39
|
+
columnSums,
|
|
39
40
|
declaresOwnMinimum,
|
|
40
41
|
inFlow,
|
|
41
42
|
mainAxisOf,
|
|
@@ -74,7 +75,9 @@ const PADDINGS = [
|
|
|
74
75
|
* main axis without wrapping, and with a height that is its content's —
|
|
75
76
|
* nothing named on the height axis (`declaresOwnMinimum`), so neither a
|
|
76
77
|
* size, a floor, a ceiling nor a clip stands between its children and its
|
|
77
|
-
* extent.
|
|
78
|
+
* extent. And one the sum has an answer for (`columnSums`): a column with a
|
|
79
|
+
* block pulled up over the one before it is measured, as a root, rather
|
|
80
|
+
* than summed into no extent and no floor.
|
|
78
81
|
*/
|
|
79
82
|
export function isSummableColumn(node) {
|
|
80
83
|
const style = node.style;
|
|
@@ -86,7 +89,8 @@ export function isSummableColumn(node) {
|
|
|
86
89
|
mainAxisOf(node) === 'height' &&
|
|
87
90
|
style.flexDirection !== 'column-reverse' &&
|
|
88
91
|
(style.flexWrap == null || style.flexWrap === 'nowrap') &&
|
|
89
|
-
!declaresOwnMinimum(node, 'height')
|
|
92
|
+
!declaresOwnMinimum(node, 'height') &&
|
|
93
|
+
columnSums(node)
|
|
90
94
|
);
|
|
91
95
|
}
|
|
92
96
|
|
|
@@ -129,6 +133,17 @@ export function spineScope(root, sources, listOnly) {
|
|
|
129
133
|
const spine = new Set();
|
|
130
134
|
const stops = new Set();
|
|
131
135
|
const offered = new OfferedWidths(root);
|
|
136
|
+
// asked once a column: a dozen rows that arrive in one column of a
|
|
137
|
+
// thousand ask it of that column a dozen times (`columnSums`)
|
|
138
|
+
const summable = new Map();
|
|
139
|
+
const sums = (column) => {
|
|
140
|
+
let answer = summable.get(column);
|
|
141
|
+
if (answer === undefined) {
|
|
142
|
+
answer = isSummableColumn(column);
|
|
143
|
+
summable.set(column, answer);
|
|
144
|
+
}
|
|
145
|
+
return answer;
|
|
146
|
+
};
|
|
132
147
|
for (const source of sources) {
|
|
133
148
|
const list = listOnly(source);
|
|
134
149
|
// the way up to where the change stops mattering
|
|
@@ -148,7 +163,7 @@ export function spineScope(root, sources, listOnly) {
|
|
|
148
163
|
let top = -1;
|
|
149
164
|
for (let i = path.length - 1; i >= 0; i--) {
|
|
150
165
|
const summed = i > 0 || list;
|
|
151
|
-
if (!summed || !
|
|
166
|
+
if (!summed || !sums(path[i])) {
|
|
152
167
|
top = i;
|
|
153
168
|
break;
|
|
154
169
|
}
|
|
@@ -379,6 +394,9 @@ function rootWidths(node, offered, stop) {
|
|
|
379
394
|
if (!stretched(node)) return null;
|
|
380
395
|
if (MARGINS.some((key) => typeof style[key] === 'string')) return null;
|
|
381
396
|
if (PADDINGS.some((key) => typeof style[key] === 'string')) return null;
|
|
397
|
+
// …nor a gap that is a share of something: laid out alone, it is a share
|
|
398
|
+
// of something else
|
|
399
|
+
if (typeof (style.rowGap ?? style.gap) === 'string') return null;
|
|
382
400
|
return offered.in(parent, stop);
|
|
383
401
|
}
|
|
384
402
|
|