react-x11 2.52.0 → 2.53.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/README.md +10 -10
- package/docs/README.md +160 -314
- package/docs/accessibility.md +176 -229
- package/docs/appearance.md +77 -176
- package/docs/click-to-component.md +18 -51
- package/docs/clipboard.md +40 -59
- package/docs/components.md +518 -1036
- package/docs/dbus.md +61 -133
- package/docs/debugging.md +152 -245
- package/docs/desktop-calendar.md +51 -88
- package/docs/desktop.md +222 -347
- package/docs/devtools.md +50 -167
- package/docs/drag-and-drop.md +126 -204
- package/docs/ecosystem/animation.md +91 -125
- package/docs/ecosystem/data-fetching.md +38 -53
- package/docs/ecosystem/dev-tooling.md +159 -202
- package/docs/ecosystem/forms.md +61 -97
- package/docs/ecosystem/headless.md +64 -89
- package/docs/ecosystem/i18n.md +18 -21
- package/docs/ecosystem/icons.md +93 -126
- package/docs/ecosystem/layout.md +42 -52
- package/docs/ecosystem/routing.md +18 -29
- package/docs/ecosystem/state.md +71 -88
- package/docs/ecosystem/testing.md +131 -157
- package/docs/ecosystem/theming.md +119 -165
- package/docs/ecosystem.md +145 -179
- package/docs/elements.md +1091 -1774
- package/docs/embedding.md +83 -132
- package/docs/events.md +412 -695
- package/docs/extending.md +784 -1326
- package/docs/eyedropper.md +63 -122
- package/docs/filedialog.md +71 -107
- package/docs/frame.md +195 -339
- package/docs/gl.md +65 -62
- package/docs/globalmenu.md +63 -146
- package/docs/macos.md +531 -3234
- package/docs/notifications.md +33 -47
- package/docs/packaging.md +166 -309
- package/docs/permissions.md +9 -14
- package/docs/react-features.md +81 -106
- package/docs/remote.md +32 -76
- package/docs/scale.md +72 -186
- package/docs/styling.md +329 -547
- package/docs/system.md +200 -280
- package/docs/testing.md +63 -83
- package/docs/typescript.md +6 -48
- package/docs/uri-schemes.md +67 -107
- package/docs/wayland-backend.md +200 -459
- package/docs/windows-integrations.md +75 -131
- package/docs/windows.md +343 -1367
- package/package.json +3 -2
- package/src/Reconciler.js +2 -2
- package/src/a11y.js +1 -1
- package/src/backend/context2d.js +3 -3
- package/src/bootstrap.js +1 -1
- package/src/cocoa/app.js +6 -6
- package/src/cocoa/bezels.js +1 -1
- package/src/cocoa/fonts.js +1 -1
- package/src/cocoa/globalmenu.js +1 -1
- package/src/cocoa/keymap.js +1 -1
- package/src/cocoa/panewindow.js +1 -1
- package/src/cocoa/player.js +1 -1
- package/src/cocoa/presenter.js +2 -2
- package/src/cocoa/promotion.js +1 -1
- package/src/cocoa/relaunch.js +1 -1
- package/src/cocoa/threaded.js +1 -1
- package/src/cocoa/video.js +1 -1
- package/src/cocoa/window.js +4 -4
- package/src/components/Menu.js +7 -2
- package/src/components/Switch.js +1 -1
- package/src/components/native.js +1 -1
- package/src/dnd.js +2 -2
- package/src/glnodes.js +1 -1
- package/src/grid.js +1 -1
- package/src/layouts.js +1 -1
- package/src/nodes/animation.js +1 -1
- package/src/nodes/invalidate.js +1 -1
- package/src/nodes/layouthost.js +1 -1
- package/src/nodes/video.js +19 -8
- package/src/nodes/window/droptarget.js +1 -1
- package/src/pacing.js +1 -1
- package/src/refresh/index.js +2 -1
- package/src/types/video.d.ts +5 -2
- package/src/videobackend.js +28 -0
- package/src/videoframes.js +29 -16
- package/src/wayland/app.js +27 -0
- package/src/wayland/video.js +376 -0
- package/src/win32/a11y.js +1 -1
- package/src/win32/app.js +2 -2
- package/src/win32/fonts.js +1 -1
- package/src/win32/glarea.js +4 -4
- package/src/win32/overlay.js +1 -1
- package/src/win32/panewindow.js +3 -3
- package/src/win32/shell.js +1 -1
- package/src/win32/window.js +2 -2
- package/src/windowid.js +1 -1
- package/src/xvideo.js +189 -0
- package/docs/architecture/accessibility.md +0 -470
- package/docs/architecture/animation.md +0 -707
- package/docs/architecture/container-queries.md +0 -525
- package/docs/architecture/custom-layout.md +0 -334
- package/docs/architecture/drag-and-drop.md +0 -941
- package/docs/architecture/element-layer-contents.md +0 -622
- package/docs/architecture/frame-pacing.md +0 -255
- package/docs/architecture/grid-layout.md +0 -358
- package/docs/architecture/protocol-efficiency.md +0 -357
- package/docs/architecture/video.md +0 -1149
- package/docs/architecture/windowed-regions.md +0 -854
- package/docs/glx.md +0 -137
- package/docs/wayland.md +0 -545
- package/docs/windows-embedding.md +0 -271
- package/docs/windows-gl.md +0 -221
package/docs/drag-and-drop.md
CHANGED
|
@@ -18,10 +18,9 @@ Both work on any drawn element and on `<window>` / `<popup>`. Their
|
|
|
18
18
|
_presence_ is what registers the node — there is no provider to mount and
|
|
19
19
|
no context to thread.
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
> This page is the reference.
|
|
21
|
+
Design notes — why the API is shaped this way, what was rejected, the
|
|
22
|
+
protocol details: [design/architecture/drag-and-drop.md](../design/architecture/drag-and-drop.md)
|
|
23
|
+
and [design/drag-and-drop.md](../design/drag-and-drop.md).
|
|
25
24
|
|
|
26
25
|
## Accepting drops
|
|
27
26
|
|
|
@@ -52,11 +51,10 @@ What the node will take. Four shapes:
|
|
|
52
51
|
| `(types) => boolean` | your own predicate, given the offered type names |
|
|
53
52
|
| _absent_ | anything — a bare `onDrop` is a valid dropzone |
|
|
54
53
|
|
|
55
|
-
The
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
[`clipboard.read('files')`](clipboard.md#reading) resolves it the same way:
|
|
54
|
+
The groups exist because the same payload arrives under different names
|
|
55
|
+
depending on who is dragging, and exact-match filtering misses most of the
|
|
56
|
+
desktop. The clipboard uses the same table, so
|
|
57
|
+
[`clipboard.read('files')`](clipboard.md#reading) resolves the same way:
|
|
60
58
|
|
|
61
59
|
| group | matches |
|
|
62
60
|
| --------- | ---------------------------------------------------------------------------------------------------------- |
|
|
@@ -71,11 +69,10 @@ catches only the last of those. Groups do not overlap: a Firefox link is
|
|
|
71
69
|
`text/x-moz-url`, which is in `uris`, so accept `['uris', 'text']` if you
|
|
72
70
|
want both. A group only ever expands to its own list.
|
|
73
71
|
|
|
74
|
-
`dropAccept` is **data, not a callback
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
which is a user-visible freeze in a program you do not own.
|
|
72
|
+
`dropAccept` is **data, not a callback**: the accept/reject answer to
|
|
73
|
+
another application is computed straight from it, without entering React,
|
|
74
|
+
because a window that is slow to answer freezes the drag cursor in _the
|
|
75
|
+
other application_.
|
|
79
76
|
|
|
80
77
|
### The drag events
|
|
81
78
|
|
|
@@ -108,7 +105,7 @@ and `dropped: false` — so releasing state there is safe.
|
|
|
108
105
|
Which node is _the_ target, when they nest: the accept decision picks the
|
|
109
106
|
**deepest** node whose `dropAccept` matches, skipping any node marked
|
|
110
107
|
`disabled`, so a `<window>`-level `dropAccept` is a fallback that loses to
|
|
111
|
-
any matching descendant. Two wrinkles
|
|
108
|
+
any matching descendant. Two wrinkles:
|
|
112
109
|
|
|
113
110
|
- `onDrop` still dispatches capture → bubble along the whole ancestor path
|
|
114
111
|
of the node under the pointer, so an ancestor with an `onDrop` sees the
|
|
@@ -149,14 +146,12 @@ rather than whether it would be taken (see [The drop](#the-drop)):
|
|
|
149
146
|
|
|
150
147
|
`onDrop` may be `async`. For a drop from **another application** the
|
|
151
148
|
protocol reply is held until the returned promise settles, so `await`ing a
|
|
152
|
-
write is correct; a watchdog sends the reply anyway after 10 s,
|
|
153
|
-
forgotten `await`
|
|
154
|
-
else's.
|
|
149
|
+
write is correct; a watchdog sends the reply anyway after 10 s, so a
|
|
150
|
+
forgotten `await` cannot hang the other application's drag.
|
|
155
151
|
|
|
156
|
-
For an **in-app** drop
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
resolved — if the source needs to know, have the handler tell it.
|
|
152
|
+
For an **in-app** drop nothing waits: `onDragEnd` fires immediately and an
|
|
153
|
+
async `onDrop` finishes on its own afterwards. If the source needs to know
|
|
154
|
+
when the handler resolved, have the handler tell it.
|
|
160
155
|
|
|
161
156
|
The event adds three ways to reach the payload:
|
|
162
157
|
|
|
@@ -187,10 +182,9 @@ onDrop={async (e) => {
|
|
|
187
182
|
applications, which is also how you tell the two apart without checking
|
|
188
183
|
`e.source`.
|
|
189
184
|
|
|
190
|
-
There is deliberately **no `e.dataTransfer
|
|
191
|
-
asynchronous and
|
|
192
|
-
|
|
193
|
-
literals with no error anywhere. The different name is the warning.
|
|
185
|
+
There is deliberately **no `e.dataTransfer`**: X selection transfer is
|
|
186
|
+
asynchronous, and a DOM lookalike whose `getData()` returned a promise
|
|
187
|
+
would stringify to `"[object Promise]"` with no error anywhere.
|
|
194
188
|
|
|
195
189
|
`onDrop` can also settle what the drop _did_, which is what the source is
|
|
196
190
|
told afterwards:
|
|
@@ -215,17 +209,13 @@ what it was given, so **the drop is refused**, exactly as if it had called
|
|
|
215
209
|
([events.md](events.md#when-a-handler-throws)): the handler name, the
|
|
216
210
|
element, and the component that rendered it.
|
|
217
211
|
|
|
218
|
-
Refusing
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
path can still `accept()`; the refusal is the failed one's answer, not the
|
|
222
|
-
whole dispatch's.
|
|
212
|
+
Refusing matters because a `move` source deletes its own copy when told
|
|
213
|
+
the drop was taken. A later handler on the path can still `accept()`; the
|
|
214
|
+
refusal is the failed one's answer, not the whole dispatch's.
|
|
223
215
|
|
|
224
216
|
An `async` failure is reported when the promise rejects. For a drop from
|
|
225
217
|
another application that is before the protocol reply goes out, so the
|
|
226
|
-
source hears about it
|
|
227
|
-
for the same reason nothing else in that path waits — which is why it is
|
|
228
|
-
reported rather than only returned.
|
|
218
|
+
source hears about it; in-app it is after `onDragEnd` has already fired.
|
|
229
219
|
|
|
230
220
|
### Sources that ask
|
|
231
221
|
|
|
@@ -248,14 +238,11 @@ onDrop={async (e) => {
|
|
|
248
238
|
`actionDescriptions` is positional and the source's own wording — a `null`
|
|
249
239
|
means it offered that action without describing it, so use your own label.
|
|
250
240
|
Both arrays are empty for every other kind of drag, so they can be read
|
|
251
|
-
without guarding
|
|
252
|
-
position actually asks.
|
|
241
|
+
without guarding.
|
|
253
242
|
|
|
254
|
-
The menu is yours to draw
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
of how long to make someone wait belongs to the app, not the toolkit.
|
|
258
|
-
`ContextMenu` is the obvious thing to build it from.
|
|
243
|
+
The menu is yours to draw (`ContextMenu` is the obvious thing to build it
|
|
244
|
+
from): the protocol reply is held while it is open, and how long the other
|
|
245
|
+
application's drag cursor waits is the app's choice.
|
|
259
246
|
|
|
260
247
|
### `:drag-over`
|
|
261
248
|
|
|
@@ -265,12 +252,9 @@ follows, resolved the same way (a repaint, no React render). For most
|
|
|
265
252
|
dropzones that is the whole highlight feature, with no handler and no
|
|
266
253
|
state.
|
|
267
254
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
drag passes over. Where the difference matters — showing "yes" versus "no"
|
|
272
|
-
feedback — use `useDropTarget` and style from `isAccepted`, which is
|
|
273
|
-
exactly the distinction it exists to draw:
|
|
255
|
+
It is **not** filtered by `dropAccept`: a zone that accepts only
|
|
256
|
+
`image/png` still highlights while a text drag passes over. For "yes"
|
|
257
|
+
versus "no" feedback, use `useDropTarget` and style from `isAccepted`:
|
|
274
258
|
|
|
275
259
|
```jsx
|
|
276
260
|
<box {...dropProps} style={[s.zone, isOver && (isAccepted ? s.yes : s.no)]} />
|
|
@@ -284,23 +268,20 @@ Nothing opts in: it applies to any scroll container a drag passes over,
|
|
|
284
268
|
from this application or another one.
|
|
285
269
|
|
|
286
270
|
The viewport it scrolls is the nearest scroll _container_ enclosing the
|
|
287
|
-
pointer,
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
([extending.md](extending.md#scrolling-content-you-painted)) — is
|
|
291
|
-
|
|
292
|
-
during a drag would only move content out of reach.
|
|
271
|
+
pointer, found the way the wheel finds one. Something that scrolls pixels
|
|
272
|
+
of its own without being a container — `<textarea>` and anything else that
|
|
273
|
+
answers `canScroll` alone
|
|
274
|
+
([extending.md](extending.md#scrolling-content-you-painted)) — is excluded:
|
|
275
|
+
nothing can be dropped into one.
|
|
293
276
|
|
|
294
|
-
Two consequences
|
|
277
|
+
Two consequences:
|
|
295
278
|
|
|
296
279
|
- **The content moves under a stationary pointer**, so `onDragEnter`,
|
|
297
280
|
`onDragLeave` and `onDragOver` keep firing while it scrolls, and
|
|
298
|
-
`:drag-over` follows.
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
application those positions are what start the scrolling in the first
|
|
303
|
-
place.
|
|
281
|
+
`:drag-over` follows.
|
|
282
|
+
- **Do not `e.freeze()` a zone inside a scroll container.** For a drag from
|
|
283
|
+
another application, the positions freezing stops are what drive the
|
|
284
|
+
scrolling.
|
|
304
285
|
|
|
305
286
|
### `useDropTarget`
|
|
306
287
|
|
|
@@ -322,11 +303,9 @@ return (
|
|
|
322
303
|
);
|
|
323
304
|
```
|
|
324
305
|
|
|
325
|
-
`accept` is the hook's name for `dropAccept` (
|
|
326
|
-
`
|
|
327
|
-
|
|
328
|
-
pass through. `isOver` is true whenever a drag is over the node,
|
|
329
|
-
`isAccepted` only when it matches.
|
|
306
|
+
`accept` is the hook's name for `dropAccept` (as in react-dropzone);
|
|
307
|
+
`onDragOver`, `onDragEnter` and `onDragLeave` pass through. `isOver` is
|
|
308
|
+
true whenever a drag is over the node, `isAccepted` only when it matches.
|
|
330
309
|
|
|
331
310
|
## Starting drags
|
|
332
311
|
|
|
@@ -361,12 +340,13 @@ happens to it:
|
|
|
361
340
|
| any other object | **by reference**, on `e.items` | `JSON.stringify`d |
|
|
362
341
|
| a function (thunk) | called on drop | called when the drag first leaves your windows |
|
|
363
342
|
|
|
364
|
-
Thunks
|
|
343
|
+
Thunks keep a payload nobody asks for from being built. Offer
|
|
365
344
|
`'text/uri-list': () => buildUriList(selection)` and the list is assembled
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
the drag
|
|
345
|
+
at the drop for an in-app drag, and the moment the pointer first leaves your
|
|
346
|
+
windows for an external one — crossing the window edge resolves every
|
|
347
|
+
thunk, whether or not the drag is ever dropped. (On win32 every thunk
|
|
348
|
+
resolves when the drag starts; see
|
|
349
|
+
[platform notes](#interoperating-with-other-applications).)
|
|
370
350
|
|
|
371
351
|
Type names are yours to choose. Use real MIME types for anything another
|
|
372
352
|
application might want (`text/uri-list` is what every file manager reads),
|
|
@@ -385,18 +365,16 @@ target is told the one action you prefer, not the list, so
|
|
|
385
365
|
choice. In-app targets see the same single requested action on `e.action`
|
|
386
366
|
and can still answer with any of them.
|
|
387
367
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
368
|
+
**There is no modifier-key negotiation.** Holding Ctrl or Shift during a
|
|
369
|
+
drag does not switch copy to move the way a desktop file manager does — the
|
|
370
|
+
requested action is fixed when the drag starts, and only the target can
|
|
371
|
+
change it.
|
|
392
372
|
|
|
393
|
-
Modifier state is readable on the **source** side
|
|
394
|
-
|
|
395
|
-
`
|
|
396
|
-
`
|
|
397
|
-
|
|
398
|
-
user is holding. Decide the action from what is being dragged and where it
|
|
399
|
-
lands.
|
|
373
|
+
Modifier state is readable on the **source** side: `e.ctrlKey` and
|
|
374
|
+
`e.shiftKey` are meaningful in `onDrag`. On the **target** side
|
|
375
|
+
(`onDragEnter`/`onDragOver`/`onDragLeave`/`onDrop`) they are always
|
|
376
|
+
`false`, so a dropzone cannot see what the user is holding — decide the
|
|
377
|
+
action from what is being dragged and where it lands.
|
|
400
378
|
|
|
401
379
|
### The gesture
|
|
402
380
|
|
|
@@ -456,16 +434,14 @@ return (
|
|
|
456
434
|
```
|
|
457
435
|
|
|
458
436
|
`position` is `{ x, y, accepted }` in screen coordinates while dragging and
|
|
459
|
-
`null` otherwise. **`dragPreview` on the `<popup>` is required**: it
|
|
460
|
-
the
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
`transparent` is
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
the window falls back to filling itself square, which is what it looked
|
|
468
|
-
like anyway. See
|
|
437
|
+
`null` otherwise. **`dragPreview` on the `<popup>` is required**: it marks
|
|
438
|
+
the window as the preview, not something under the pointer — without it
|
|
439
|
+
the drag would immediately land on its own preview.
|
|
440
|
+
|
|
441
|
+
`transparent` is optional but usually wanted: the popup is a real window,
|
|
442
|
+
so a card with a `borderRadius` shows the window's ground in its corners
|
|
443
|
+
unless the window is transparent. Where no compositor is running the window
|
|
444
|
+
falls back to filling itself square. See
|
|
469
445
|
[`transparent`](elements.md#transparent--rounded-corners-and-translucency).
|
|
470
446
|
|
|
471
447
|
## The two transports
|
|
@@ -483,17 +459,14 @@ apart unless they ask.
|
|
|
483
459
|
|
|
484
460
|
The switch is automatic and reversible: leave your windows and the drag is
|
|
485
461
|
promoted to XDND; come back and it demotes again. Reorderable lists, trees
|
|
486
|
-
and kanban boards
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
On the [cocoa backend](macos.md) the external transport is AppKit's
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
(`e.source === 'internal'`, `e.items` live), so the table above holds. What
|
|
495
|
-
another application reads is the pasteboard, where a `dragData` thunk is a
|
|
496
|
-
promise the system asks for only when a consumer actually reads that type.
|
|
462
|
+
and kanban boards cost nothing on the wire, while the _same_ component can
|
|
463
|
+
also be dropped into GIMP.
|
|
464
|
+
|
|
465
|
+
On the [cocoa backend](macos.md) the external transport is AppKit's drag
|
|
466
|
+
session, and the table still holds: a drop on one of your own windows
|
|
467
|
+
arrives with `e.source === 'internal'` and `e.items` live. Another
|
|
468
|
+
application reads the pasteboard, where a `dragData` thunk is resolved only
|
|
469
|
+
when a consumer actually reads that type.
|
|
497
470
|
|
|
498
471
|
## Interoperating with other applications
|
|
499
472
|
|
|
@@ -513,75 +486,46 @@ managers, editors and upload widgets will take it.
|
|
|
513
486
|
Platform notes:
|
|
514
487
|
|
|
515
488
|
- **Linux / XWayland** — works both directions, including to and from
|
|
516
|
-
native Wayland applications
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
differ from X11. The payload is read **at the drop** (the pasteboard is the
|
|
547
|
-
source's promise, and it may withdraw it once its session ends), so
|
|
548
|
-
`getData()` answers from that read. And the gesture runs inside AppKit's
|
|
549
|
-
own tracking loop, which owns the thread from the threshold to the
|
|
550
|
-
release: no timer of ours fires and no frame clock ticks in between, so
|
|
551
|
-
every frame of a drag is painted from the callback that reported it, and
|
|
552
|
-
every render a drag schedules — a `<popup dragPreview>` following
|
|
553
|
-
`useDragSource().position`, a `useDropTarget` hint, an insertion marker —
|
|
554
|
-
is dispatched at discrete priority and landed there. The preview follows
|
|
555
|
-
the pointer as it does on X11, and because a `<popup>` here is a
|
|
556
|
-
non-activating panel at the pop-up-menu window level — above every other
|
|
557
|
-
application's windows, which sit at the normal level — it is what the
|
|
558
|
-
desktop sees while the pointer is over Finder or a browser. It is also
|
|
559
|
-
the first window the window server finds when the pointer comes back
|
|
560
|
-
over one of yours, and AppKit does not look past it: a window with no
|
|
561
|
-
dragged types registered is still the one found, the drag then has no
|
|
562
|
-
destination, and the window beneath never hears of it. So the popup is
|
|
563
|
-
made transparent to the pointer — `dragPreview` sets the bridge's
|
|
564
|
-
`ignoresMouseEvents` (@windowkit/appkit 0.6.0) — and the hit passes
|
|
565
|
-
through it to the window it covers: the same exclusion the router makes
|
|
566
|
-
on X11, made where the window server can see it. It registers no
|
|
567
|
-
pasteboard types either, having nothing to accept with. What AppKit's own
|
|
568
|
-
session carries is still a blank image: the drag has no bitmap of the
|
|
569
|
-
system's, only the preview you draw. For the same reason the session's
|
|
570
|
-
slide-back is off: left on, AppKit animates that blank image home over
|
|
571
|
-
about a second _before_ it reports the release, so a drop nothing took
|
|
572
|
-
reached `onDragEnd` — and the preview stayed frozen where the pointer let
|
|
573
|
-
go — only after a pause spent animating nothing. The release ends the
|
|
574
|
-
gesture the moment it happens, as on X11, and a return animation is the
|
|
575
|
-
app's to draw from `onDragEnd`.
|
|
489
|
+
native Wayland applications.
|
|
490
|
+
- **The Wayland backend** — works both directions, over `wl_data_device`.
|
|
491
|
+
As on the cocoa backend, the payload is read at the drop.
|
|
492
|
+
- **Windows, the win32 backend** — works both directions, over OLE. Files
|
|
493
|
+
arrive as CF_HDROP and become one `text/uri-list`, text as
|
|
494
|
+
CF_UNICODETEXT, and any other type as a clipboard format registered under
|
|
495
|
+
its MIME name — which two react-x11 apps agree on and nothing else will.
|
|
496
|
+
Two differences: a `dragData` **thunk is not lazy** — every type's bytes
|
|
497
|
+
are resolved when the drag starts — and the shell's "may this be dropped
|
|
498
|
+
here" (the cursor) is answered one motion late. The drop itself is
|
|
499
|
+
answered exactly.
|
|
500
|
+
- **macOS, the cocoa backend** — works both directions: Finder files arrive
|
|
501
|
+
as `['files']` (one `text/uri-list`), text as `['text']`, a Safari URL as
|
|
502
|
+
`['uris']`. MIME names map onto UTIs through the OS's type database; a
|
|
503
|
+
private `application/x-myapp-…` travels between two react-x11 apps as a
|
|
504
|
+
`dyn.*` type. Differences from X11:
|
|
505
|
+
- The payload is read **at the drop**, and `getData()` answers from that
|
|
506
|
+
read.
|
|
507
|
+
- The gesture runs inside AppKit's tracking loop, from the threshold to
|
|
508
|
+
the release: **no timer fires and no frame clock ticks** in between.
|
|
509
|
+
Renders a drag schedules (a `<popup dragPreview>` following
|
|
510
|
+
`useDragSource().position`, a `useDropTarget` hint, an insertion marker)
|
|
511
|
+
are dispatched at discrete priority and painted from the drag callback.
|
|
512
|
+
- The `<popup dragPreview>` sits above every other application's windows,
|
|
513
|
+
so it stays visible over the Finder or a browser, and lets the pointer
|
|
514
|
+
pass through to the window beneath. The system draws no drag image of
|
|
515
|
+
its own — only your preview.
|
|
516
|
+
- There is no slide-back animation: the release reaches `onDragEnd` the
|
|
517
|
+
moment it happens, as on X11, and a return animation is the app's to
|
|
518
|
+
draw from there.
|
|
576
519
|
- **macOS / XQuartz** — X client to X client works. Dragging from **Finder**
|
|
577
|
-
into an X11 window does not
|
|
578
|
-
[XQuartz#173](https://github.com/XQuartz/XQuartz/issues/173)
|
|
579
|
-
|
|
580
|
-
the cocoa backend, which has the Finder.
|
|
520
|
+
into an X11 window does not
|
|
521
|
+
([XQuartz#173](https://github.com/XQuartz/XQuartz/issues/173)); test
|
|
522
|
+
against another X client, or run the cocoa backend.
|
|
581
523
|
- **Over `ssh -X`** — each pointer position during an external drag costs a
|
|
582
|
-
few round trips
|
|
583
|
-
|
|
584
|
-
|
|
524
|
+
few round trips, so a drag feels heavier on a link than a click does.
|
|
525
|
+
In-app drags cost nothing extra. See [remote.md](remote.md).
|
|
526
|
+
|
|
527
|
+
Design notes on each platform:
|
|
528
|
+
[design/drag-and-drop.md](../design/drag-and-drop.md#interoperating-with-other-applications).
|
|
585
529
|
|
|
586
530
|
## Testing
|
|
587
531
|
|
|
@@ -606,8 +550,12 @@ await act(async () => {
|
|
|
606
550
|
|
|
607
551
|
`onDragStart`, `onDrop` and `onDragEnd` all run, and `e.items` carries the
|
|
608
552
|
live payload. Drops from _other_ applications need a second X connection
|
|
609
|
-
playing the foreign source — `test/dnd.test.js` in
|
|
610
|
-
|
|
553
|
+
playing the foreign source — `test/dnd.test.js` in the react-x11 repository
|
|
554
|
+
shows the pattern. See [testing.md](testing.md).
|
|
555
|
+
|
|
556
|
+
Runnable demonstrations of both halves, as two separate processes, are
|
|
557
|
+
[`examples/dnd-source.jsx`](../examples/dnd-source.jsx) and
|
|
558
|
+
[`examples/dnd-target.jsx`](../examples/dnd-target.jsx).
|
|
611
559
|
|
|
612
560
|
## Limits and defaults
|
|
613
561
|
|
|
@@ -620,44 +568,18 @@ that if you need the pattern. See [testing.md](testing.md).
|
|
|
620
568
|
| XDND protocol version | 5 (the current one; older sources are negotiated down) |
|
|
621
569
|
| requires | ntk ≥ 5.4.0 |
|
|
622
570
|
|
|
571
|
+
On X11 every top-level window advertises XDND, whether or not it mounts a
|
|
572
|
+
drop target; one with none mounted refuses drags over it. Child windows
|
|
573
|
+
(`<glarea>`, a nested `<window>`) and a `<popup dragPreview>` never
|
|
574
|
+
advertise — drags over a child arrive at its top-level and are routed down.
|
|
575
|
+
|
|
623
576
|
Not supported, deliberately or not yet:
|
|
624
577
|
|
|
625
578
|
- **Motif drag and drop**, the pre-XDND protocol. Some legacy Motif and
|
|
626
579
|
AWT applications speak only that and will not interoperate.
|
|
627
580
|
- **XdndDirectSave (XDS)** — "drag out of the app to save a file into the
|
|
628
581
|
file manager". A separate protocol layered on XDND.
|
|
629
|
-
- **A built-in menu for `XdndActionAsk
|
|
630
|
-
|
|
631
|
-
to draw. A built-in one would have to hold the reply while it was open,
|
|
632
|
-
and the reply is what keeps another application's drag from freezing.
|
|
582
|
+
- **A built-in menu for `XdndActionAsk`** — the choices are handed to you
|
|
583
|
+
([Sources that ask](#sources-that-ask)); the menu is yours to draw.
|
|
633
584
|
- **`react-dnd`** and other DOM drag libraries — see
|
|
634
585
|
[ecosystem.md](ecosystem.md).
|
|
635
|
-
|
|
636
|
-
## Protocol notes
|
|
637
|
-
|
|
638
|
-
For anyone watching the wire, or debugging against another toolkit:
|
|
639
|
-
|
|
640
|
-
- `XdndAware` (version 5) is written on every **top-level** window
|
|
641
|
-
unconditionally at realize time. Child windows — `<glarea>`, a nested
|
|
642
|
-
`<window>` — never advertise, per XDND v3+; drags over them arrive at the
|
|
643
|
-
top-level and are routed down in JavaScript. A `<popup dragPreview>`
|
|
644
|
-
never advertises either: it follows the pointer, and a window that is
|
|
645
|
-
never under it has nothing to accept.
|
|
646
|
-
- A window with **no drop targets mounted** answers with a refusal covering
|
|
647
|
-
the whole window, so a well-behaved source stops asking while the pointer
|
|
648
|
-
stays inside it. What an app that never uses drag and drop still pays:
|
|
649
|
-
the `XdndAware` property, one batch of atom interns, and a session object
|
|
650
|
-
per top-level window, all once at realize — plus one refusal per drag
|
|
651
|
-
that crosses it.
|
|
652
|
-
- The drop conversion uses the timestamp from `XdndDrop`, as the protocol
|
|
653
|
-
requires, and `XdndSelection` is released at drag end with the timestamp
|
|
654
|
-
it was acquired with (ICCCM 2.3.1).
|
|
655
|
-
- As a source, the foreign window under the pointer is resolved per motion
|
|
656
|
-
frame rather than from a cache built at drag start — correct, and a few
|
|
657
|
-
round trips heavier on a remote display.
|
|
658
|
-
|
|
659
|
-
The reasoning behind each of those, and the measurements that produced
|
|
660
|
-
them, are in [architecture/drag-and-drop.md](architecture/drag-and-drop.md).
|
|
661
|
-
Runnable demonstrations of both halves, as two separate processes, are
|
|
662
|
-
[`examples/dnd-source.jsx`](../examples/dnd-source.jsx) and
|
|
663
|
-
[`examples/dnd-target.jsx`](../examples/dnd-target.jsx).
|