~ubuntu-branches/ubuntu/wily/qtbase-opensource-src/wily

« back to all changes in this revision

Viewing changes to examples/widgets/doc/src/tetrix.qdoc

  • Committer: Package Import Robot
  • Author(s): Timo Jyrinki
  • Date: 2013-02-05 12:46:17 UTC
  • Revision ID: package-import@ubuntu.com-20130205124617-c8jouts182j002fx
Tags: upstream-5.0.1+dfsg
ImportĀ upstreamĀ versionĀ 5.0.1+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
 
4
** Contact: http://www.qt-project.org/legal
 
5
**
 
6
** This file is part of the documentation of the Qt Toolkit.
 
7
**
 
8
** $QT_BEGIN_LICENSE:FDL$
 
9
** Commercial License Usage
 
10
** Licensees holding valid commercial Qt licenses may use this file in
 
11
** accordance with the commercial license agreement provided with the
 
12
** Software or, alternatively, in accordance with the terms contained in
 
13
** a written agreement between you and Digia.  For licensing terms and
 
14
** conditions see http://qt.digia.com/licensing.  For further information
 
15
** use the contact form at http://qt.digia.com/contact-us.
 
16
**
 
17
** GNU Free Documentation License Usage
 
18
** Alternatively, this file may be used under the terms of the GNU Free
 
19
** Documentation License version 1.3 as published by the Free Software
 
20
** Foundation and appearing in the file included in the packaging of
 
21
** this file.  Please review the following information to ensure
 
22
** the GNU Free Documentation License version 1.3 requirements
 
23
** will be met: http://www.gnu.org/copyleft/fdl.html.
 
24
** $QT_END_LICENSE$
 
25
**
 
26
****************************************************************************/
 
27
 
 
28
/*! 
 
29
    \example widgets/tetrix
 
30
    \title Tetrix Example
 
31
    \ingroup examples-widgets
 
32
    \brief The Tetrix example is a Qt version of the classic Tetrix game.
 
33
 
 
34
    \image tetrix-example.png
 
35
 
 
36
    The object of the game is to stack pieces dropped from the top of the
 
37
    playing area so that they fill entire rows at the bottom of the playing area.
 
38
 
 
39
    When a row is filled, all the blocks on that row are removed, the player earns
 
40
    a number of points, and the pieces above are moved down to occupy that row.
 
41
    If more than one row is filled, the blocks on each row are removed, and the
 
42
    player earns extra points.
 
43
 
 
44
    The \uicontrol{Left} cursor key moves the current piece one space to the left, the
 
45
    \uicontrol{Right} cursor key moves it one space to the right, the \uicontrol{Up} cursor
 
46
    key rotates the piece counter-clockwise by 90 degrees, and the \uicontrol{Down}
 
47
    cursor key rotates the piece clockwise by 90 degrees.
 
48
 
 
49
    To avoid waiting for a piece to fall to the bottom of the board, press \uicontrol{D}
 
50
    to immediately move the piece down by one row, or press the \uicontrol{Space} key to
 
51
    drop it as close to the bottom of the board as possible.
 
52
 
 
53
    This example shows how a simple game can be created using only three classes:
 
54
 
 
55
    \list
 
56
    \li The \c TetrixWindow class is used to display the player's score, number of
 
57
       lives, and information about the next piece to appear.
 
58
    \li The \c TetrixBoard class contains the game logic, handles keyboard input, and
 
59
       displays the pieces on the playing area.
 
60
    \li The \c TetrixPiece class contains information about each piece.
 
61
    \endlist
 
62
 
 
63
    In this approach, the \c TetrixBoard class is the most complex class, since it
 
64
    handles the game logic and rendering. One benefit of this is that the
 
65
    \c TetrixWindow and \c TetrixPiece classes are very simple and contain only a
 
66
    minimum of code.
 
67
 
 
68
    \section1 TetrixWindow Class Definition
 
69
 
 
70
    The \c TetrixWindow class is used to display the game information and contains
 
71
    the playing area:
 
72
 
 
73
    \snippet widgets/tetrix/tetrixwindow.h 0
 
74
 
 
75
    We use private member variables for the board, various display widgets, and
 
76
    buttons to allow the user to start a new game, pause the current game, and quit.
 
77
 
 
78
    Although the window inherits QWidget, the constructor does not provide an
 
79
    argument to allow a parent widget to be specified. This is because the window
 
80
    will always be used as a top-level widget.
 
81
 
 
82
    \section1 TetrixWindow Class Implementation
 
83
 
 
84
    The constructor sets up the user interface elements for the game:
 
85
 
 
86
    \snippet widgets/tetrix/tetrixwindow.cpp 0
 
87
 
 
88
    We begin by constructing a \c TetrixBoard instance for the playing area and a
 
89
    label that shows the next piece to be dropped into the playing area; the label
 
90
    is initially empty.
 
91
 
 
92
    Three QLCDNumber objects are used to display the score, number of lives, and
 
93
    lines removed. These initially show default values, and will be filled in
 
94
    when a game begins:
 
95
 
 
96
    \snippet widgets/tetrix/tetrixwindow.cpp 1
 
97
 
 
98
    Three buttons with shortcuts are constructed so that the user can start a
 
99
    new game, pause the current game, and quit the application:
 
100
 
 
101
    \snippet widgets/tetrix/tetrixwindow.cpp 2
 
102
    \snippet widgets/tetrix/tetrixwindow.cpp 3
 
103
 
 
104
    These buttons are configured so that they never receive the keyboard focus;
 
105
    we want the keyboard focus to remain with the \c TetrixBoard instance so that
 
106
    it receives all the keyboard events. Nonetheless, the buttons will still respond
 
107
    to \uicontrol{Alt} key shortcuts.
 
108
 
 
109
    We connect \l{QAbstractButton::}{clicked()} signals from the \uicontrol{Start}
 
110
    and \uicontrol{Pause} buttons to the board, and from the \uicontrol{Quit} button to the
 
111
    application's \l{QApplication::}{quit()} slot.
 
112
 
 
113
    \snippet widgets/tetrix/tetrixwindow.cpp 4
 
114
    \snippet widgets/tetrix/tetrixwindow.cpp 5
 
115
 
 
116
    Signals from the board are also connected to the LCD widgets for the purpose of
 
117
    updating the score, number of lives, and lines removed from the playing area.
 
118
 
 
119
    We place the label, LCD widgets, and the board into a QGridLayout
 
120
    along with some labels that we create with the \c createLabel() convenience
 
121
    function:
 
122
 
 
123
    \snippet widgets/tetrix/tetrixwindow.cpp 6
 
124
 
 
125
    Finally, we set the grid layout on the widget, give the window a title, and
 
126
    resize it to an appropriate size.
 
127
 
 
128
    The \c createLabel() convenience function simply creates a new label on the
 
129
    heap, gives it an appropriate alignment, and returns it to the caller:
 
130
 
 
131
    \snippet widgets/tetrix/tetrixwindow.cpp 7
 
132
 
 
133
    Since each label will be used in the widget's layout, it will become a child
 
134
    of the \c TetrixWindow widget and, as a result, it will be deleted when the
 
135
    window is deleted.
 
136
 
 
137
    \section1 TetrixPiece Class Definition
 
138
 
 
139
    The \c TetrixPiece class holds information about a piece in the game's
 
140
    playing area, including its shape, position, and the range of positions it can
 
141
    occupy on the board:
 
142
 
 
143
    \snippet widgets/tetrix/tetrixpiece.h 0
 
144
 
 
145
    Each shape contains four blocks, and these are defined by the \c coords private
 
146
    member variable. Additionally, each piece has a high-level description that is
 
147
    stored internally in the \c pieceShape variable.
 
148
 
 
149
    The constructor is written inline in the definition, and simply ensures that
 
150
    each piece is initially created with no shape. The \c shape() function simply
 
151
    returns the contents of the \c pieceShape variable, and the \c x() and \c y()
 
152
    functions return the x and y-coordinates of any given block in the shape.
 
153
 
 
154
    \section1 TetrixPiece Class Implementation
 
155
 
 
156
    The \c setRandomShape() function is used to select a random shape for a piece:
 
157
 
 
158
    \snippet widgets/tetrix/tetrixpiece.cpp 0
 
159
 
 
160
    For convenience, it simply chooses a random shape from the \c TetrixShape enum
 
161
    and calls the \c setShape() function to perform the task of positioning the
 
162
    blocks.
 
163
 
 
164
    The \c setShape() function uses a look-up table of pieces to associate each
 
165
    shape with an array of block positions:
 
166
 
 
167
    \snippet widgets/tetrix/tetrixpiece.cpp 1
 
168
    \snippet widgets/tetrix/tetrixpiece.cpp 2
 
169
 
 
170
    These positions are read from the table into the piece's own array of positions,
 
171
    and the piece's internal shape information is updated to use the new shape.
 
172
 
 
173
    The \c x() and \c y() functions are implemented inline in the class definition,
 
174
    returning positions defined on a grid that extends horizontally and vertically
 
175
    with coordinates from -2 to 2. Although the predefined coordinates for each
 
176
    piece only vary horizontally from -1 to 1 and vertically from -1 to 2, each
 
177
    piece can be rotated by 90, 180, and 270 degrees.
 
178
 
 
179
    The \c minX() and \c maxX() functions return the minimum and maximum horizontal
 
180
    coordinates occupied by the blocks that make up the piece:
 
181
 
 
182
    \snippet widgets/tetrix/tetrixpiece.cpp 3
 
183
    \snippet widgets/tetrix/tetrixpiece.cpp 4
 
184
 
 
185
    Similarly, the \c minY() and \c maxY() functions return the minimum and maximum
 
186
    vertical coordinates occupied by the blocks:
 
187
 
 
188
    \snippet widgets/tetrix/tetrixpiece.cpp 5
 
189
    \snippet widgets/tetrix/tetrixpiece.cpp 6
 
190
 
 
191
    The \c rotatedLeft() function returns a new piece with the same shape as an
 
192
    existing piece, but rotated counter-clockwise by 90 degrees:
 
193
 
 
194
    \snippet widgets/tetrix/tetrixpiece.cpp 7
 
195
 
 
196
    Similarly, the \c rotatedRight() function returns a new piece with the same
 
197
    shape as an existing piece, but rotated clockwise by 90 degrees:
 
198
 
 
199
    \snippet widgets/tetrix/tetrixpiece.cpp 9
 
200
 
 
201
    These last two functions enable each piece to create rotated copies of itself.
 
202
 
 
203
    \section1 TetrixBoard Class Definition
 
204
 
 
205
    The \c TetrixBoard class inherits from QFrame and contains the game logic and display features:
 
206
 
 
207
    \snippet widgets/tetrix/tetrixboard.h 0
 
208
 
 
209
    Apart from the \c setNextPieceLabel() function and the \c start() and \c pause()
 
210
    public slots, we only provide public functions to reimplement QWidget::sizeHint()
 
211
    and QWidget::minimumSizeHint(). The signals are used to communicate changes to
 
212
    the player's information to the \c TetrixWindow instance.
 
213
 
 
214
    The rest of the functionality is provided by reimplementations of protected event
 
215
    handlers and private functions:
 
216
 
 
217
    \snippet widgets/tetrix/tetrixboard.h 1
 
218
 
 
219
    The board is composed of a fixed-size array whose elements correspond to
 
220
    spaces for individual blocks. Each element in the array contains a \c TetrixShape
 
221
    value corresponding to the type of shape that occupies that element.
 
222
 
 
223
    Each shape on the board will occupy four elements in the array, and these will
 
224
    all contain the enum value that corresponds to the type of the shape.
 
225
 
 
226
    We use a QBasicTimer to control the rate at which pieces fall toward the bottom
 
227
    of the playing area. This allows us to provide an implementation of
 
228
    \l{QObject::}{timerEvent()} that we can use to update the widget.
 
229
 
 
230
    \section1 TetrixBoard Class Implementation
 
231
 
 
232
    In the constructor, we customize the frame style of the widget, ensure that
 
233
    keyboard input will be received by the widget by using Qt::StrongFocus for the
 
234
    focus policy, and initialize the game state:
 
235
 
 
236
    \snippet widgets/tetrix/tetrixboard.cpp 0
 
237
 
 
238
    The first (next) piece is also set up with a random shape.
 
239
 
 
240
    The \c setNextPieceLabel() function is used to pass in an externally-constructed
 
241
    label to the board, so that it can be shown alongside the playing area:
 
242
 
 
243
    \snippet widgets/tetrix/tetrixboard.cpp 1
 
244
 
 
245
    We provide a reasonable size hint and minimum size hint for the board, based on
 
246
    the size of the space for each block in the playing area:
 
247
 
 
248
    \snippet widgets/tetrix/tetrixboard.cpp 2
 
249
    \snippet widgets/tetrix/tetrixboard.cpp 3
 
250
 
 
251
    By using a minimum size hint, we indicate to the layout in the parent widget
 
252
    that the board should not shrink below a minimum size.
 
253
 
 
254
    A new game is started when the \c start() slot is called. This resets the
 
255
    game's state, the player's score and level, and the contents of the board:
 
256
 
 
257
    \snippet widgets/tetrix/tetrixboard.cpp 4
 
258
 
 
259
    We also emit signals to inform other components of these changes before creating
 
260
    a new piece that is ready to be dropped into the playing area. We start the
 
261
    timer that determines how often the piece drops down one row on the board.
 
262
 
 
263
    The \c pause() slot is used to temporarily stop the current game by stopping the
 
264
    internal timer:
 
265
 
 
266
    \snippet widgets/tetrix/tetrixboard.cpp 5
 
267
    \snippet widgets/tetrix/tetrixboard.cpp 6
 
268
 
 
269
    We perform checks to ensure that the game can only be paused if it is already
 
270
    running and not already paused.
 
271
 
 
272
    The \c paintEvent() function is straightforward to implement. We begin by
 
273
    calling the base class's implementation of \l{QWidget::}{paintEvent()} before
 
274
    constructing a QPainter for use on the board:
 
275
 
 
276
    \snippet widgets/tetrix/tetrixboard.cpp 7
 
277
 
 
278
    Since the board is a subclass of QFrame, we obtain a QRect that covers the area
 
279
    \e inside the frame decoration before drawing our own content.
 
280
 
 
281
    If the game is paused, we want to hide the existing state of the board and
 
282
    show some text. We achieve this by painting text onto the widget and returning
 
283
    early from the function. The rest of the painting is performed after this point.
 
284
 
 
285
    The position of the top of the board is found by subtracting the total height
 
286
    of each space on the board from the bottom of the frame's internal rectangle.
 
287
    For each space on the board that is occupied by a piece, we call the
 
288
    \c drawSquare() function to draw a block at that position.
 
289
 
 
290
    \snippet widgets/tetrix/tetrixboard.cpp 8
 
291
    \snippet widgets/tetrix/tetrixboard.cpp 9
 
292
 
 
293
    Spaces that are not occupied by blocks are left blank.
 
294
 
 
295
    Unlike the existing pieces on the board, the current piece is drawn
 
296
    block-by-block at its current position:
 
297
 
 
298
    \snippet widgets/tetrix/tetrixboard.cpp 10
 
299
    \snippet widgets/tetrix/tetrixboard.cpp 11
 
300
    \snippet widgets/tetrix/tetrixboard.cpp 12
 
301
 
 
302
    The \c keyPressEvent() handler is called whenever the player presses a key while
 
303
    the \c TetrixBoard widget has the keyboard focus.
 
304
 
 
305
    \snippet widgets/tetrix/tetrixboard.cpp 13
 
306
 
 
307
    If there is no current game, the game is running but paused, or if there is no
 
308
    current shape to control, we simply pass on the event to the base class.
 
309
 
 
310
    We check whether the event is about any of the keys that the player uses to
 
311
    control the current piece and, if so, we call the relevant function to handle
 
312
    the input:
 
313
 
 
314
    \snippet widgets/tetrix/tetrixboard.cpp 14
 
315
 
 
316
    In the case where the player presses a key that we are not interested in, we
 
317
    again pass on the event to the base class's implementation of
 
318
    \l{QWidget::}{keyPressEvent()}.
 
319
 
 
320
    The \c timerEvent() handler is called every time the class's QBasicTimer
 
321
    instance times out. We need to check that the event we receive corresponds to
 
322
    our timer. If it does, we can update the board:
 
323
 
 
324
    \snippet widgets/tetrix/tetrixboard.cpp 15
 
325
    \snippet widgets/tetrix/tetrixboard.cpp 16
 
326
    \snippet widgets/tetrix/tetrixboard.cpp 17
 
327
 
 
328
    If a row (or line) has just been filled, we create a new piece and reset the
 
329
    timer; otherwise we move the current piece down by one row. We let the base
 
330
    class handle other timer events that we receive.
 
331
 
 
332
    The \c clearBoard() function simply fills the board with the
 
333
    \c TetrixShape::NoShape value:
 
334
 
 
335
    \snippet widgets/tetrix/tetrixboard.cpp 18
 
336
 
 
337
    The \c dropDown() function moves the current piece down as far as possible on
 
338
    the board, either until it is touching the bottom of the playing area or it is
 
339
    stacked on top of another piece:
 
340
 
 
341
    \snippet widgets/tetrix/tetrixboard.cpp 19
 
342
    \snippet widgets/tetrix/tetrixboard.cpp 20
 
343
 
 
344
    The number of rows the piece has dropped is recorded and passed to the
 
345
    \c pieceDropped() function so that the player's score can be updated.
 
346
 
 
347
    The \c oneLineDown() function is used to move the current piece down by one row
 
348
    (line), either when the user presses the \uicontrol{D} key or when the piece is
 
349
    scheduled to move:
 
350
 
 
351
    \snippet widgets/tetrix/tetrixboard.cpp 21
 
352
 
 
353
    If the piece cannot drop down by one line, we call the \c pieceDropped() function
 
354
    with zero as the argument to indicate that it cannot fall any further, and that
 
355
    the player should receive no extra points for the fall.
 
356
 
 
357
    The \c pieceDropped() function itself is responsible for awarding points to the
 
358
    player for positioning the current piece, checking for full rows on the board
 
359
    and, if no lines have been removed, creating a new piece to replace the current
 
360
    one:
 
361
 
 
362
    \snippet widgets/tetrix/tetrixboard.cpp 22
 
363
    \snippet widgets/tetrix/tetrixboard.cpp 23
 
364
 
 
365
    We call \c removeFullLines() each time a piece has been dropped. This scans
 
366
    the board from bottom to top, looking for blank spaces on each row.
 
367
 
 
368
    \snippet widgets/tetrix/tetrixboard.cpp 24
 
369
    \snippet widgets/tetrix/tetrixboard.cpp 25
 
370
    \snippet widgets/tetrix/tetrixboard.cpp 26
 
371
    \snippet widgets/tetrix/tetrixboard.cpp 27
 
372
 
 
373
    If a row contains no blank spaces, the rows above it are copied down by one row
 
374
    to compress the stack of pieces, the top row on the board is cleared, and the
 
375
    number of full lines found is incremented.
 
376
 
 
377
    \snippet widgets/tetrix/tetrixboard.cpp 28
 
378
    \snippet widgets/tetrix/tetrixboard.cpp 29
 
379
 
 
380
    If some lines have been removed, the player's score and the total number of lines
 
381
    removed are updated. The \c linesRemoved() and \c scoreChanged() signals are
 
382
    emitted to send these new values to other widgets in the window.
 
383
 
 
384
    Additionally, we set the timer to elapse after half a second, set the
 
385
    \c isWaitingAfterLine flag to indicate that lines have been removed, unset
 
386
    the piece's shape to ensure that it is not drawn, and update the widget.
 
387
    The next time that the \c timerEvent() handler is called, a new piece will be
 
388
    created and the game will continue.
 
389
 
 
390
    The \c newPiece() function places the next available piece at the top of the
 
391
    board, and creates a new piece with a random shape:
 
392
 
 
393
    \snippet widgets/tetrix/tetrixboard.cpp 30
 
394
    \snippet widgets/tetrix/tetrixboard.cpp 31
 
395
 
 
396
    We place a new piece in the middle of the board at the top. The game is over if
 
397
    the piece can't move, so we unset its shape to prevent it from being drawn, stop
 
398
    the timer, and unset the \c isStarted flag.
 
399
 
 
400
    The \c showNextPiece() function updates the label that shows the next piece to
 
401
    be dropped:
 
402
 
 
403
    \snippet widgets/tetrix/tetrixboard.cpp 32
 
404
    \snippet widgets/tetrix/tetrixboard.cpp 33
 
405
 
 
406
    We draw the piece's component blocks onto a pixmap that is then set on the label.
 
407
 
 
408
    The \c tryMove() function is used to determine whether a piece can be positioned
 
409
    at the specified coordinates:
 
410
 
 
411
    \snippet widgets/tetrix/tetrixboard.cpp 34
 
412
 
 
413
    We examine the spaces on the board that the piece needs to occupy and, if they
 
414
    are already occupied by other pieces, we return \c false to indicate that the
 
415
    move has failed.
 
416
 
 
417
    \snippet widgets/tetrix/tetrixboard.cpp 35
 
418
 
 
419
    If the piece could be placed on the board at the desired location, we update the
 
420
    current piece and its position, update the widget, and return \c true to indicate
 
421
    success.
 
422
 
 
423
    The \c drawSquare() function draws the blocks (normally squares) that make up
 
424
    each piece using different colors for pieces with different shapes:
 
425
 
 
426
    \snippet widgets/tetrix/tetrixboard.cpp 36
 
427
 
 
428
    We obtain the color to use from a look-up table that relates each shape to an
 
429
    RGB value, and use the painter provided to draw the block at the specified
 
430
    coordinates.
 
431
*/