Drag-drop & clipboard (1.41)¶
Copy-ready patterns for file drop, text copy, and paste. Prefer these over ad-hoc DropArea / invisible TextEdit helpers scattered through the app.
| Surface | Module | Role |
|---|---|---|
FileDropZone |
Extras | Fluent drop target (text/uri-list) |
CopyButton |
Extras | Copy + success glyph feedback |
WindowHelper.copyText / clipboardText |
Platform | Programmatic clipboard R/W |
FilePicker |
Platform | Browse fallback when users prefer a dialog |
Gallery: FileDropZone · CopyButton · System integration (FilePicker).
Related: system-integration.md · window-helper.md · platform-linux-wayland.md · compatibility-1xx.md.
File drop recipe¶
import QWinUI3.Extras
import QWinUI3.Platform
FileDropZone {
title: qsTr("Drop images here")
subtitle: qsTr("PNG / JPEG — or use Browse")
acceptExtensions: [".png", ".jpg", ".jpeg", ".webp"]
onFilesDropped: function (urls) {
// urls are QUrl strings (often file:///…)
for (var i = 0; i < urls.length; ++i)
console.log(urls[i])
}
}
Button {
text: qsTr("Browse…")
onClicked: FilePicker.openFiles(
qsTr("Open images"),
["Images (*.png *.jpg *.jpeg *.webp)", "All (*.*)"],
function (paths) {
if (!paths || !paths.length)
return
// Same handler as drop — normalize to file URLs if needed
},
Window.window)
}
| Topic | Detail |
|---|---|
| MIME | DropArea.keys includes text/uri-list + optional acceptMimeTypes (2.13) — OS file managers / Explorer / Nautilus |
| Filter | acceptExtensions — lowercase suffixes; empty = accept all URLs |
| MIME filter | acceptMimeTypes — e.g. ["image/png", "image/*"]; defense-in-depth when OS reports formats — security-trust.md 2.13 |
| Reject | Non-matching drops are ignored; isDragRejected + dragRejected signal (2.57) highlight bad MIME/types |
| Browse | Wire FilePicker beside the zone — drop is not enough for keyboard / a11y / touch users (touch-pointer.md 1.57) |
| Paths | Drop gives URLs; FilePicker gives native paths — normalize in one place |
Win / Linux notes¶
| Host | Expectation |
|---|---|
| Windows | Explorer → app drop works with text/uri-list. Always pass Window.window to FilePicker. |
| Linux X11 | Same DropArea path. Portal FilePicker gets parent_window. |
| Linux Wayland | DnD still uses Qt DropArea; FilePicker portal parent — pass Window.window; 2.57 focus fallback when omitted — platform-linux-wayland.md · files-linux-257.md |
| Out of scope (1.41) | Full OLE / complex shell DnD, dragging out of the app, custom non-file MIME productization |
Do not invent a second drop chrome — restyle via title / subtitle / symbol / Theme tokens.
Clipboard — copy¶
UI control (preferred in toolbars / forms)¶
CopyButton {
textToCopy: apiKeyField.text
onCopyCompleted: function (t) { /* toast / status */ }
onCopyFailed: { /* empty payload */ }
}
CopyButton uses an offscreen TextEdit copy path (works without importing Platform). Icon-only: omit text / set empty text.
Programmatic (shell / C++ bridge)¶
| Use when | Prefer |
|---|---|
| Visible “Copy” affordance | CopyButton |
| Copy from code / menu / CommandPalette | WindowHelper.copyText |
| Hex / color samples | ColorPicker.copyHex() — pickers.md |
Clipboard — paste¶
TextField {
id: field
placeholderText: qsTr("Paste here")
}
Button {
text: qsTr("Paste")
onClicked: {
var t = WindowHelper.clipboardText()
if (t.length)
field.text = t
}
}
| Topic | Detail |
|---|---|
| Focus | Standard TextField / TextArea already handle Ctrl+V |
| Explicit Paste | Use WindowHelper.clipboardText() for custom Paste actions |
| Password | Respect PasswordBox paste policies (canPasteClipboardContent) — Gallery PasswordBox page |
| Secrets | Prefer CopyButton feedback; avoid logging clipboard contents |
Pairing drop + clipboard + picker¶
Typical import surface:
- Drop files onto
FileDropZone - Browse via
FilePicker.openFiles(same processing function) - Copy path of the last import with
CopyButton/WindowHelper.copyText - Optional:
WindowHelper.revealFileInFolder(path)after save — shell-extras.md
function ingestPaths(paths) { /* shared */ }
FileDropZone {
onFilesDropped: function (urls) {
var paths = []
for (var i = 0; i < urls.length; ++i)
paths.push(String(urls[i]).replace(/^file:\/\//, "")) // simplify as needed
ingestPaths(paths)
}
}
URL → local path conversion should use your Qt helpers (QUrl.toLocalFile from C++, or a small QML util)—do not assume stripping file:// is enough on Windows.
Checklist¶
- [ ]
acceptExtensionsmatches what FilePicker filters show - [ ] Browse button (or equivalent) next to every drop zone
- [ ] One ingest function for drop + dialog
- [ ] Copy uses
CopyButtonorWindowHelper.copyText— not a one-off TextEdit - [ ] Linux: tested under Wayland or documented as X11-validated — see Wayland matrix
Out of scope: OLE compound documents, browser HTML DnD as a product API, mobile share sheets.
Export / print the other direction: print-share.md (1.63) — grabToImage → save → reveal.
Trust: security-trust.md (1.64) — never auto-execute drops; keep filters tight.