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