| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the documentation of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:FDL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Free Documentation License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Free |
| ** Documentation License version 1.3 as published by the Free Software |
| ** Foundation and appearing in the file included in the packaging of |
| ** this file. Please review the following information to ensure |
| ** the GNU Free Documentation License version 1.3 requirements |
| ** will be met: https://www.gnu.org/licenses/fdl-1.3.html. |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| /*! |
| \example draganddrop/fridgemagnets |
| \title Fridge Magnets Example |
| \brief The Fridge Magnets example illustrates how to move around several types of |
| MIME-encoded data with drag and drop. |
| |
| The Fridge Magnets example shows how to supply more than one type |
| of MIME-encoded data with a drag and drop operation. |
| |
| \image fridgemagnets-example.png |
| |
| With this application the user can play around with a collection |
| of fridge magnets, using drag and drop to form new sentences from |
| the words on the magnets. The example consists of two classes: |
| |
| \list |
| \li \c DragLabel is a custom widget representing one |
| single fridge magnet. |
| \li \c DragWidget provides the main application window. |
| \endlist |
| |
| We will first take a look at the \c DragLabel class, then we will |
| examine the \c DragWidget class. |
| |
| \section1 DragLabel Class Definition |
| |
| Each fridge magnet is represented by an instance of the \c |
| DragLabel class: |
| |
| \snippet draganddrop/fridgemagnets/draglabel.h 0 |
| |
| Each instance of this QLabel subclass will be used to display an |
| pixmap generated from a text string. Since we cannot store both |
| text and a pixmap in a standard label, we declare a private variable |
| to hold the original text, and we define an additional member |
| function to allow it to be accessed. |
| |
| \section1 DragLabel Class Implementation |
| |
| In the \c DragLabel constructor, we first create a QImage object |
| on which we will draw the fridge magnet's text and frame: |
| |
| \snippet draganddrop/fridgemagnets/draglabel.cpp 0 |
| |
| Its size depends on the current font size, and its format is |
| QImage::Format_ARGB32_Premultiplied; i.e., the image is stored |
| using a premultiplied 32-bit ARGB format (0xAARRGGBB). |
| |
| We then construct a font object that uses the application's |
| default font, and set its style strategy. The style strategy tells |
| the font matching algorithm what type of fonts should be used to |
| find an appropriate default family. The QFont::ForceOutline forces |
| the use of outline fonts. |
| |
| To draw the text and frame onto the image, we use the QPainter |
| class. QPainter provides highly optimized methods to do most of |
| the drawing GUI programs require. It can draw everything from |
| simple lines to complex shapes like pies and chords. It can also |
| draw aligned text and pixmaps. |
| |
| \snippet draganddrop/fridgemagnets/draglabel.cpp 1 |
| |
| A painter can be activated by passing a paint device to the |
| constructor, or by using the \l{QPainter::}{begin()} method as we |
| do in this example. The \l{QPainter::}{end()} method deactivates |
| it. Note that the latter function is called automatically upon |
| destruction when the painter is actived by its constructor. The |
| QPainter::Antialiasing render hint ensures that the paint engine |
| will antialias the edges of primitives if possible. |
| |
| When the painting is done, we convert our image to a pixmap using |
| QPixmap's \l {QPixmap::}{fromImage()} method. This method also |
| takes an optional flags argument, and converts the given image to |
| a pixmap using the specified flags to control the conversion (the |
| flags argument is a bitwise-OR of the Qt::ImageConversionFlags; |
| passing 0 for flags sets all the default options). |
| |
| \snippet draganddrop/fridgemagnets/draglabel.cpp 2 |
| |
| Finally, we set the label's \l{QLabel::pixmap}{pixmap property} |
| and store the label's text for later use. |
| |
| \e{Note that setting the pixmap clears any previous content, including |
| any text previously set using QLabel::setText(), and disables |
| the label widget's buddy shortcut, if any.} |
| |
| \section1 DragWidget Class Definition |
| |
| The \c DragWidget class inherits QWidget, providing support for |
| drag and drop operations: |
| |
| \snippet draganddrop/fridgemagnets/dragwidget.h 0 |
| |
| To make the widget responsive to drag and drop operations, we simply |
| reimplement the \l{QWidget::}{dragEnterEvent()}, |
| \l{QWidget::}{dragMoveEvent()} and \l{QWidget::}{dropEvent()} event |
| handlers inherited from QWidget. |
| |
| We also reimplement \l{QWidget::}{mousePressEvent()} to make the |
| widget responsive to mouse clicks. This is where we will write code |
| to start drag and drop operations. |
| |
| \section1 DragWidget Class Implementation |
| |
| In the constructor, we first open the file containing the words on |
| our fridge magnets: |
| |
| \snippet draganddrop/fridgemagnets/dragwidget.cpp 0 |
| |
| QFile is an I/O device for reading and writing text and binary |
| files and resources, and may be used by itself or in combination |
| with QTextStream or QDataStream. We have chosen to read the |
| contents of the file using the QTextStream class that provides a |
| convenient interface for reading and writing text. |
| |
| We then create the fridge magnets. As long as there is data (the |
| QTextStream::atEnd() method returns true if there is no more data |
| to be read from the stream), we read one line at a time using |
| QTextStream's \l {QTextStream::}{readLine()} method. |
| |
| \snippet draganddrop/fridgemagnets/dragwidget.cpp 1 |
| |
| For each line, we create a \c DragLabel object using the read line |
| as text, we calculate its position and ensure that it is visible by |
| calling the QWidget::show() method. We set the Qt::WA_DeleteOnClose |
| attribute on each label to ensure that any unused labels will be |
| deleted; we will need to create new labels and delete old ones when |
| they are dragged around, and this ensures that the example does not |
| leak memory. |
| |
| We also set the \c FridgeMagnets widget's palette, minimum size |
| and window title. |
| |
| \snippet draganddrop/fridgemagnets/dragwidget.cpp 2 |
| |
| Finally, to enable our user to move the fridge magnets around, we |
| must also set the \c FridgeMagnets widget's |
| \l{QWidget::acceptDrops}{acceptDrops} property. |
| |
| \snippet draganddrop/fridgemagnets/dragwidget.cpp 3 |
| |
| Setting this property to true announces to the system that this |
| widget \e may be able to accept drop events (events that are sent |
| when drag and drop actions are completed). Later, we will |
| implement the functions that ensure that the widget accepts the |
| drop events it is interested in. |
| |
| \section2 Dragging |
| |
| Let's take a look at the \l{QWidget::}{mousePressEvent()} event |
| handler, where drag and drop operations begin: |
| |
| \snippet draganddrop/fridgemagnets/dragwidget.cpp 13 |
| \snippet draganddrop/fridgemagnets/dragwidget.cpp 14 |
| |
| Mouse events occur when a mouse button is pressed or released |
| inside a widget, or when the mouse cursor is moved. By |
| reimplementing the \l{QWidget::}{mousePressEvent()} method we |
| ensure that we will receive mouse press events for the widget |
| containing the fridge magnets. |
| |
| Whenever we receive such an event, we first check to see if the |
| position of the click coincides with one of the labels. If not, |
| we simply return. |
| |
| If the user clicked a label, we determine the position of the |
| \e{hot spot} (the position of the click relative to the top-left |
| corner of the label). We create a byte array to store the label's |
| text and the hot spot, and we use a QDataStream object to stream |
| the data into the byte array. |
| |
| With all the information in place, we create a new QMimeData object. |
| As mentioned above, QMimeData objects associate the data that they |
| hold with the corresponding MIME types to ensure that information |
| can be safely transferred between applications. The |
| \l{QMimeData::}{setData()} method sets the data associated with a |
| given MIME type. In our case, we associate our item data with the |
| custom \c application/x-fridgemagnet type. |
| |
| \snippet draganddrop/fridgemagnets/dragwidget.cpp 15 |
| |
| Note that we also associate the magnet's text with the |
| \c text/plain MIME type using QMimeData's \l{QMimeData::}{setText()} |
| method. Below, we will see how our widget detects both these MIME |
| types with its event handlers. |
| |
| Finally, we create a QDrag object. It is the QDrag class that |
| handles most of the details of a drag and drop operation, |
| providing support for MIME-based drag and drop data transfer. The |
| data to be transferred by the drag and drop operation is contained |
| in a QMimeData object. When we call QDrag's |
| \l{QDrag::}{setMimeData()} method the ownership of our item data is |
| transferred to the QDrag object. |
| |
| We call the \l{QDrag::}{setPixmap()} function to set the pixmap used |
| to represent the data during the drag and drop operation. |
| Typically, this pixmap shows an icon that represents the MIME type |
| of the data being transferred, but any pixmap can be used. In this |
| example, we simply use the pixmap used by the label itself to make |
| it look like the fridge magnet itself is being moved. |
| |
| \snippet draganddrop/fridgemagnets/dragwidget.cpp 16 |
| |
| We also specify the cursor's hot spot, its position relative to the |
| top-level corner of the drag pixmap, to be the point we calculated |
| above. This makes the process of dragging the label feel more natural |
| because the cursor always points to the same place on the label |
| during the drag operation. |
| |
| We start the drag operation using QDrag's \l{QDrag::}{exec()} function, |
| requesting that the magnet is copied when the drag is completed. |
| |
| \snippet draganddrop/fridgemagnets/dragwidget.cpp 17 |
| |
| The function returns the drop action actually performed by the user |
| (this can be either a copy or a move action in this case); if this |
| action is equal to Qt::MoveAction we will close the activated |
| fridge magnet widget because we will create a new one to replace it |
| (see the \l{drop}{dropEvent()} implementation). Otherwise, if |
| the drop is outside our main widget, we simply show the widget in |
| its original position. |
| |
| \section2 Dropping |
| |
| When a a drag and drop action enters our widget, we will receive a |
| drag enter \e event. QDragEnterEvent inherits most of its |
| functionality from QDragMoveEvent, which in turn inherits most of |
| its functionality from QDropEvent. Note that we must accept this |
| event in order to receive the drag move events that are sent while |
| the drag and drop action is in progress. The drag enter event is |
| always immediately followed by a drag move event. |
| |
| In our \c dragEnterEvent() implementation, we first determine |
| whether we support the event's MIME type or not: |
| |
| \snippet draganddrop/fridgemagnets/dragwidget.cpp 4 |
| \snippet draganddrop/fridgemagnets/dragwidget.cpp 5 |
| \snippet draganddrop/fridgemagnets/dragwidget.cpp 6 |
| |
| If the type is \c application/x-fridgemagnet and the event |
| origins from any of this application's fridge magnet widgets, we |
| first set the event's drop action using the |
| QDropEvent::setDropAction() method. An event's drop action is the |
| action to be performed on the data by the target. Qt::MoveAction |
| indicates that the data is moved from the source to the target. |
| |
| Then we call the event's \l {QDragMoveEvent::}{accept()} method to |
| indicate that we have handled the event. In general, unaccepted |
| events might be propagated to the parent widget. If the event |
| origins from any other widget, we simply accept the proposed |
| action. |
| |
| \snippet draganddrop/fridgemagnets/dragwidget.cpp 7 |
| |
| We also accept the proposed action if the event's MIME type is \c |
| text/plain, i.e., if QMimeData::hasText() returns true. If the |
| event has any other type, on the other hand, we call the event's |
| \l {QDragMoveEvent::}{ignore()} method allowing the event to be |
| propagated further. |
| |
| \snippet draganddrop/fridgemagnets/dragwidget.cpp 8 |
| |
| Drag move events occur when the cursor enters a widget, when it |
| moves within the widget, and when a modifier key is pressed on the |
| keyboard while the widget has focus. Our widget will receive drag |
| move events repeatedly while a drag is within its boundaries. We |
| reimplement the \l {QWidget::}{dragMoveEvent()} method, and |
| examine the event in the exact same way as we did with drag enter |
| events. |
| |
| Note that the \l{QWidget::}{dropEvent()} event handler behaves |
| slightly differently: We first get hold of the event's MIME |
| data. |
| |
| \target drop |
| \snippet draganddrop/fridgemagnets/dragwidget.cpp 9 |
| |
| The QMimeData class provides a container for data that |
| records information about its MIME type. QMimeData objects |
| associate the data that they hold with the corresponding MIME |
| types to ensure that information can be safely transferred between |
| applications, and copied around within the same application. |
| |
| We retrieve the data associated with the \c application/x-fridgemagnet |
| MIME type using a data stream in order to create a new \c DragLabel |
| object. |
| |
| \snippet draganddrop/fridgemagnets/dragwidget.cpp 10 |
| |
| The QDataStream class provides serialization of binary data to a |
| QIODevice (a data stream is a binary stream of encoded information |
| which is completely independent of the host computer's operating |
| system, CPU or byte order). |
| |
| Finally, we create a label and move it to the event's position: |
| |
| \snippet draganddrop/fridgemagnets/dragwidget.cpp 11 |
| |
| If the source of the event is also the widget receiving the |
| drop event, we set the event's drop action to Qt::MoveAction and |
| call the event's \l{QDragMoveEvent::}{accept()} |
| method. Otherwise, we simply accept the proposed action. This |
| means that labels are moved rather than copied in the same |
| window. However, if we drag a label to a second instance of the |
| Fridge Magnets example, the default action is to copy it, leaving |
| the original in the first instance. |
| |
| If the event's MIME type is \c text/plain (i.e., if |
| QMimeData::hasText() returns true) we retrieve its text and split |
| it into words. For each word we create a new \c DragLabel action, |
| and show it at the event's position plus an offset depending on |
| the number of words in the text. In the end we accept the proposed |
| action. This lets the user drop selected text from a text editor or |
| Web browser onto the widget to add more fridge magnets. |
| |
| \snippet draganddrop/fridgemagnets/dragwidget.cpp 12 |
| |
| If the event has any other type, we call the event's |
| \l{QDragMoveEvent::}{ignore()} method allowing the event to be |
| propagated further. |
| |
| \section1 Summary |
| |
| We set our main widget's \l{QWidget::}{acceptDrops} property |
| and reimplemented QWidget's \l{QWidget::}{dragEnterEvent()}, |
| \l{QWidget::}{dragMoveEvent()} and \l{QWidget::}{dropEvent()} event |
| handlers to support content dropped on our widget. |
| |
| In addition, we reimplemented the \l{QWidget::}{mousePressEvent()} |
| function to let the user pick up fridge magnets in the first place. |
| |
| Because data is communicated using drag and drop operations and |
| encoded using MIME types, you can run more than one instance of this |
| example, and transfer magnets between them. |
| */ |