summaryrefslogtreecommitdiff
path: root/doc/src/examples/fridgemagnets.qdoc
blob: 98811b847f8ad099baa2688df42e61536065496a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Free Documentation License
** 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.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/

/*!
    \example draganddrop/fridgemagnets
    \title Fridge Magnets Example

    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
        \o \c DragLabel is a custom widget representing one
           single fridge magnet.
        \o \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 examples/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 examples/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 examples/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 examples/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 examples/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 examples/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 examples/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 examples/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 examples/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 examples/draganddrop/fridgemagnets/dragwidget.cpp 13
    \snippet examples/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 examples/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 examples/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 examples/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 examples/draganddrop/fridgemagnets/dragwidget.cpp 4
    \snippet examples/draganddrop/fridgemagnets/dragwidget.cpp 5
    \snippet examples/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 examples/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 examples/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 examples/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 examples/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 examples/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 examples/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.
*/