drag-and-drop-wayland
tauri-plugin-drag-and-drop-wayland#
Starts native drag and drop operations on Wayland, so files and data can be dropped outside the application: on the file manager, on a terminal, on an editor or on the browser. The mouse movement fires the drag, because Wayland requires the request to come from inside an event dispatch.
- Crate:
tauri-plugin-drag-and-drop-wayland - JS package:
@vasakgroup/plugin-drag-and-drop-wayland - Platform: Linux with GTK 3 and WebKitGTK. It targets Wayland; the X11-specific signals are handled too
Installation#
| |
| |
| |
Permissions#
drag-and-drop-wayland:default, in the capabilities file:
| |
The default permission enables the only command, start_drag. There are also
drag-and-drop-wayland:allow-start-drag and
drag-and-drop-wayland:deny-start-drag to grant or deny it separately.
JavaScript API#
startDrag(item, icon?, options?, onEvent?): Promise<void>#
Arms a drag and hands it over to the next mouse movement. It is called with the
button still pressed, from the gesture that starts it, which is what makes the
compositor accept it. The promise resolves once the drag is armed; the outcome
arrives through onEvent.
item takes two shapes:
- An array of file paths (
["/home/who/report.pdf"]), which travel astext/uri-listand astext/plain. - An object
{ data, types }, to drag loose data:datais the same text for every type, or a map that gives each MIME type its own, andtypesis the list of MIME types that are offered.
icon is the icon that follows the pointer: an absolute path to an image file,
or its contents in base64. Which of the two it is is decided by the contents: if
it is an absolute path and the file exists, it is read from disk; otherwise it is
decoded as base64. If it cannot be loaded, the drag goes out anyway, without an
icon.
options.mode is "copy" or "move" (default "copy") and does not change
what is offered to the destination: both actions are always offered, because on
Wayland the destination is the one that chooses, and with none in common the
compositor cancels the drag instead of delivering it. The parameter is what the
interface uses to draw the copy or move badge while the pointer is inside the
window.
onEvent receives a CallbackPayload when the drag ends:
result:"Dropped"or"Cancelled".action:"copy","move"ornull. It is informative.sourceShouldDelete: whether the destination confirmed that it kept what was delivered and the source is the one that has to remove it. This is the signal that authorizes deleting, and notaction: a destination may choose move and then not ask for the deletion, and deleting because"move"was seen would lose a file nobody copied anywhere.cursorPos: where the pointer was at the end, in screen coordinates.
Rust commands#
start_drag(app, window, item, image, options, on_event)— Arms a drag so that the next mouse movement fires it: it takes the application handle and the window, what is going to be delivered, the optional icon, the options and the channel through which the outcome is reported. It is registered ascommands::start_dragand from the interface it is invoked asplugin:drag-and-drop-wayland|start_drag, which is whereitem,image,optionsandonEventcome from.
Example#
| |
Implementation details#
- A Tauri command cannot start a drag:
gtk_drag_beginneeds to be inside an event dispatch to take the mouse grab serial from it, and without that serial the compositor discards the request. That is whystart_dragarms the drag and amotion-notify-eventhandler on theWebKitWebViewfires it. - An armed drag is valid for 1500 ms. If the movement does not arrive, because the button was released along the way, it is cancelled and reported through the channel instead of staying pending for the next movement.
COPYandMOVEare always offered; neverLINKorASK, which would promise a link the source does not know how to fulfill.- Files are delivered as
text/uri-listwith the URIs encoded as the specification requires (spaces,#and?escaped) and astext/plainwith the paths as they are: a terminal or a text field pastes what it receives, and a pasted encoded URI is not a file. - The handlers are hooked once per widget and never disconnected: the destination asks for the data after the drop, and disconnecting at the end left the transfer with nobody to serve it. That way the drag keeps working after a WebView reload.
- The end is reported only once, because
drag-failedanddrag-endmay both arrive. - On Wayland there is no signal that asks to delete the original:
drag-data-deletearrives only on X11. What is used is the pair the protocol defines —it ended without failing and the negotiated action was move—, and a failure wins over everything else. - The behavior was measured with the probes in
examples/on Wayfire.