Chapter 29. Clipboard and Drag and Drop

This chapter reveals why clipboard and drag-and-drop aren’t minor details, but the foundation of UX. Here you’ll discover how professional Qt developers build data exchange so it works fast, reliably, and identically—within the application and between programs. You’ll learn the secret of how to avoid “breaking” MIME types and hidden logic leaks that are invisible during development but costly after release.

The chapter covers QClipboard, QDrag, and QMimeData, examines 5 key MIME categories, and shows how to correctly initiate drag operations.

If drag & drop in your code currently works “by luck”—postponing this chapter is dangerous.

This chapter includes ready-to-use code examples.

Chapter Self-Check

Why should there be only one QClipboard class object in an application, and who is responsible for creating it?Answer
Correct answer: The QClipboard object is created automatically when the application starts and exists as a single instance, as it represents the system clipboard—a shared resource for all applications in the operating system.
Why is the QApplication::startDragDistance() method called in mouseMoveEvent handler instead of just checking for mouse movement?Answer
Correct answer: This distinguishes intentional dragging from accidental hand trembling—dragging starts only if the cursor has moved more than 4 pixels from the mouse button press point.
What does the MIME type application/* mean and in what cases is it used when dragging?Answer
Correct answer: This is a type for application-specific data that can only be interpreted by that application and is not intended for exchange with other programs. Used for internal dragging mechanisms.
Why is it necessary to call base methods QWidget::mousePressEvent() and QWidget::mouseMoveEvent() after adding custom logic in drag implementation?Answer
Correct answer: This preserves the base functionality of the parent class (focus handling, scrolling, system actions). Without these calls, we completely replace parent behavior, which can lead to unforeseen consequences.
Who is responsible for destroying the QDrag object after the drag operation completes?Answer
Correct answer: Qt’s drag manager takes responsibility for destroying the QDrag object in any case—regardless of whether the object was successfully dropped in the receiving zone or not.
What role does the acceptProposedAction() method play in dragEnterEvent and what happens if you don’t call it?Answer
Correct answer: The method informs the system that the widget is ready to accept the dragged object, which is visually reflected by changing the cursor. Without this call, the cursor will remain crossed out and dropEvent won’t be called when the mouse button is released.
Why can pointers be passed directly when creating custom drag types within an application, instead of copying data to QByteArray?Answer
Correct answer: The application works in a single address space, so pointers to objects remain valid and accessible in any part of the program. This increases efficiency by avoiding intermediate data copying.
What happens if you override dragEnterEvent in a widget but forget to call setAcceptDrops(true) in the constructor?Answer
Correct answer: The widget won’t receive dragEnterEvent and dropEvent at all, as Qt by default doesn’t send drag events to widgets that haven’t declared readiness to accept them.
Why does the setData() method of the QMimeData class take the mimeType string as the first parameter?Answer
Correct answer: The mimeType string serves as a data type identifier, allowing the receiving side to determine if it can correctly handle this data and access it using the same identifier.
What values can be passed to the exec() method of the QDrag class and how do they affect the visual representation of the operation?Answer
Correct answer: You can pass Qt::CopyAction (copy), Qt::MoveAction (move, default), or Qt::LinkAction (create link). These values affect the icon appearance next to the cursor, explaining the action’s meaning.
Why is hasFormat() used to check data type in dragEnterEvent instead of direct string comparison?Answer
Correct answer: The hasFormat() method correctly handles the complete MIME type structure (type/subtype) and can account for multiple data formats, making the check more reliable and flexible.
What happens if during dragging the distance between the current and initial position is less than startDragDistance()?Answer
Correct answer: The drag operation won’t start, and mouse movement will be interpreted as normal cursor movement or accidental hand trembling, preventing unintentional object dragging.
Why is setData(mimeType(), QByteArray()) called with an empty array when creating custom MIME types if data is passed via pointer?Answer
Correct answer: The setData() call registers the MIME type in the QMimeData object, allowing the hasFormat() method to correctly determine the presence of this data type when checking in dragEnterEvent.
Why is the setText() method of the QClipboard class used more often than the universal setMimeData()?Answer
Correct answer: The setText() method is simpler to use for the common case of working with text and automatically creates and configures a QMimeData object with the correct MIME type (text/plain), eliminating the need to do this manually.
What problem does using dynamic_cast solve when retrieving data in dropEvent from a custom MIME class?Answer
Correct answer: Dynamic_cast safely verifies that the received object is indeed an instance of our WidgetMimeData class, not just base QMimeData, preventing errors when attempting to call specific methods of a non-existent class.

Practical Exercises

Easy Level

Text Notepad with Clipboard
Create a simple application with two text fields (QTextEdit) and three buttons: “Copy,” “Cut,” and “Paste.” When clicking the buttons, appropriate clipboard operations should be performed for selected text in the active field. Add hotkeys Ctrl+C, Ctrl+X, and Ctrl+V.
Hints: Use QApplication::clipboard() to access the clipboard. Methods setText() and text() simplify text work. For hotkeys, use QShortcut or override keyPressEvent(). The textCursor().selectedText() method helps get selected text from QTextEdit.

Medium Level

Drag & Drop Image Gallery
Create an application with two areas (QLabel or QListWidget). The first area should display a list of image thumbnails that can be dragged to the second area. When dropping an image in the second area, it should display at full size. Implement visual feedback during dragging (change cursor or add semi-transparent preview).
Hints: Use QMimeData::setImageData() to transfer images. Override mousePressEvent() and mouseMoveEvent() for the source, dragEnterEvent() and dropEvent() for the receiver. The setPixmap() method of QDrag object allows setting a preview. Don’t forget to call setAcceptDrops(true) for the receiving widget.

Hard Level

Interface Builder with Drag and Drop
Develop a simplified visual interface builder. Create a palette with various widgets (QPushButton, QLabel, QLineEdit) and a workspace (QWidget with setAcceptDrops). Implement dragging widgets from the palette to the workspace with creation of new instances at drop point. Add the ability to move already placed widgets within the workspace. Implement a custom MIME type for transferring widget type information.
Hints: Create a custom class inherited from QMimeData to store widget type. Use QMetaObject for dynamic widget creation by class name. In dropEvent(), determine drop position via pe->position(). For moving existing widgets, use the move() method. Distinguish drag from palette and drag within workspace by drag source.

💬 Join the Discussion!

Figured out clipboard and drag & drop mechanisms? Have questions about working with MIME types correctly or implementing custom drag types?

Share your experience applying drag and drop in real projects, discuss pitfalls of working with QClipboard, or ask questions about the chapter material!

Leave a Reply

Your email address will not be published. Required fields are marked *