DragLabel Class Implementation
In the DragLabel constructor, we first create a QImage object on which we will draw the fridge magnet's text and frame:
DragLabel::DragLabel(const QString &text, QWidget *parent)
: QLabel(parent)
{
QFontMetrics metric(font());
QSize size = metric.size(Qt::TextSingleLine, text);
QImage image(size.width() + 12, size.height() + 12,
QImage::Format_ARGB32_Premultiplied);
image.fill(qRgba(0, 0, 0, 0));
QFont font;
font.setStyleStrategy(QFont::ForceOutline);
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)).
Then we constructs 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.
QPainter painter;
painter.begin(&image);
painter.setRenderHint(QPainter::Antialiasing);
painter.setBrush(Qt::white);
painter.drawRoundedRect(QRectF(0.5, 0.5, image.width()-1, image.height()-1),
25, 25, Qt::RelativeSize);
painter.setFont(font);
painter.setBrush(Qt::black);
painter.drawText(QRect(QPoint(6, 6), size), Qt::AlignCenter, text);
painter.end();
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.
A painter can be activated by passing a paint device to the constructor, or by using the begin() method as we do in this example. The 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.
setPixmap(QPixmap::fromImage(image));
labelText = text;
}
When the painting is done, we convert our image to a pixmap using QPixmap's 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).
Finally, we set the label's pixmap property and store the label's text for later use. Note that setting the pixmap clears any previous content, and disables the label widget's buddy shortcut, if any.
Now, let's take a look at the mousePressEvent() event handler:
void DragLabel::mousePressEvent(QMouseEvent *event)
QByteArray itemData;
QDataStream dataStream(&itemData, QIODevice::WriteOnly);
dataStream << labelText << QPoint(event->pos() - rect().topLeft());
Mouse events occur when a mouse button is pressed or released inside a widget, or when the mouse cursor is moved. By reimplementing the mousePressEvent() method we ensure that we will receive mouse press events for the fridge magnet widget.
Whenever we receive such an event, we will first create a byte array to store our item data, and a QDataStream object to stream the data to the byte array.
QMimeData *mimeData = new QMimeData;
mimeData->setData("application/x-fridgemagnet", itemData);
mimeData->setText(labelText);
Then 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 setData() method sets the data associated with a given MIME type. In our case, we associate our item data with the custom "application/x-fridgemagnet" type.
Note that we also associate the magnet's text with the text/plain MIME type using QMimeData's setText() method. We have already seen how our main widget detects both these MIME types with its event handlers.
QDrag *drag = new QDrag(this);
drag->setMimeData(mimeData);
drag->setHotSpot(event->pos() - rect().topLeft());
drag->setPixmap(*pixmap());
hide();
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 setMimeData() method the ownership of our item data is transferred to the QDrag object.
We also specify the cursor's hot spot, i.e., its position while the drag is in progress, to be the top-left corner of our fridge magnet. We call the setPixmap() method 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 have chosen to use the fridge magnet image itself to make the magnet appear as moving, immediately hiding the activated widget.
if (drag->exec(Qt::MoveAction | Qt::CopyAction, Qt::CopyAction) == Qt::MoveAction)
close();
else
show();
}
Then we start the drag using QDrag's exec() method requesting that the magnet is moved when the drag is completed. The method returns the performed drop action; if this action is equal to Qt::MoveAction we will close the activated fridge magnet widget because we then create a new one (with the same data) at the drop position (see the implementation of our main widgets dropEvent() method). Otherwise, if the drop is outside our main widget, we simply show the widget in its original position.