Skip to content

Drag and Drop

Use DragArea and DropArea to move data between parts of the UI with a drag-and-drop gesture. On platforms that support it, a DropArea also accepts drops from other applications.

The payload is a data-transfer value, which abstracts over the file-type transfer mechanisms supported by each platform. data-transfer values are opaque in Slint code: construct and read them via callbacks implemented in the host language.

export global Api {
pure callback string-to-transfer(string) -> data-transfer;
pure callback transfer-to-string(data-transfer) -> string;
pure callback can-drop(data-transfer) -> bool;
}
export component Example inherits Window {
width: 300px;
height: 200px;
VerticalLayout {
spacing: 8px;
padding: 8px;
Rectangle {
background: #f0c000;
DragArea {
data: Api.string-to-transfer("Hello World");
allow-copy: true;
}
Text { text: "Drag me"; }
}
Rectangle {
background: drop.contains-drag ? #80e080 : #a0a0a0;
drop := DropArea {
can-drop(event) => {
Api.can-drop(event.data) ? DragAction.copy : DragAction.none
}
dropped(event) => {
debug("Got: ", Api.transfer-to-string(event.data));
return event.proposed-action;
}
}
Text { text: "Drop here"; }
}
}
}
slint

Both can-drop and dropped return a DragAction: DragAction.copy, DragAction.move, DragAction.link to accept with that action, or DragAction.none to reject. can-drop runs during hover (drives the cursor and current-action); dropped runs on release and the returned action is what gets reported back to the source via drag-finished.

A DragArea declares which actions it permits (allow-copy, allow-move, allow-link). At least one must be set to true; a DragArea that permits no action never starts a drag (the compiler warns). When no modifier key is pressed, the proposed action is the first allowed of move, copy, link; modifier keys request a specific action (Ctrl -> copy, Shift -> move, Ctrl+Shift -> link). On macOS, use Command (⌘) instead of Ctrl. The target picks the final action in can-drop (for hover) and dropped (final answer), the runtime clamps each against the source’s allowed set, and when the drop completes the source’s drag-finished(action) fires so a “move” source can remove the original data.

DragArea {
data: Api.make-data();
allow-copy: false;
allow-move: true;
// Dim the source while the drag is in flight.
opacity: self.dragging ? 0.4 : 1.0;
drag-finished(action) => {
if action == DragAction.move {
Api.remove-source();
}
}
}
DropArea {
can-drop(event) => event.allow-move ? DragAction.move : DragAction.none;
dropped(event) => {
Api.accept-drop(event.data);
return event.proposed-action;
}
}
slint

The hovering target’s chosen action is also exposed as current-action, useful for visual feedback (a different cursor, icon, or highlight per action).

fn main() {
let app = Example::new();
// For simple plain text and image transfers, a simple conversion is supplied
// which handles the common case.
let api = app.global::<Api>();
api.on_string_to_transfer(Into::into);
// A helper for reading plain text from a `DataTransfer` is also supplied.
api.on_transfer_to_string(|data| data.plain_text().unwrap_or_default());
api.on_can_drop(|data| {
// This helper abstracts over various "plain text" MIME types
data.has_plain_text()
});
// ...
}
rust

© 2026 SixtyFPS GmbH