1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the examples of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
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 The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51#include "tetrixboard.h"
52
53#include <QKeyEvent>
54#include <QLabel>
55#include <QPainter>
56
57//! [0]
58TetrixBoard::TetrixBoard(QWidget *parent)
59 : QFrame(parent), isStarted(false), isPaused(false)
60{
61 setFrameStyle(QFrame::Panel | QFrame::Sunken);
62 setFocusPolicy(Qt::StrongFocus);
63 clearBoard();
64
65 nextPiece.setRandomShape();
66}
67//! [0]
68
69//! [1]
70void TetrixBoard::setNextPieceLabel(QLabel *label)
71{
72 nextPieceLabel = label;
73}
74//! [1]
75
76//! [2]
77QSize TetrixBoard::sizeHint() const
78{
79 return QSize(BoardWidth * 15 + frameWidth() * 2,
80 BoardHeight * 15 + frameWidth() * 2);
81}
82
83QSize TetrixBoard::minimumSizeHint() const
84//! [2] //! [3]
85{
86 return QSize(BoardWidth * 5 + frameWidth() * 2,
87 BoardHeight * 5 + frameWidth() * 2);
88}
89//! [3]
90
91//! [4]
92void TetrixBoard::start()
93{
94 if (isPaused)
95 return;
96
97 isStarted = true;
98 isWaitingAfterLine = false;
99 numLinesRemoved = 0;
100 numPiecesDropped = 0;
101 score = 0;
102 level = 1;
103 clearBoard();
104
105 emit linesRemovedChanged(numLines: numLinesRemoved);
106 emit scoreChanged(score);
107 emit levelChanged(level);
108
109 newPiece();
110 timer.start(msec: timeoutTime(), obj: this);
111}
112//! [4]
113
114//! [5]
115void TetrixBoard::pause()
116{
117 if (!isStarted)
118 return;
119
120 isPaused = !isPaused;
121 if (isPaused) {
122 timer.stop();
123 } else {
124 timer.start(msec: timeoutTime(), obj: this);
125 }
126 update();
127//! [5] //! [6]
128}
129//! [6]
130
131//! [7]
132void TetrixBoard::paintEvent(QPaintEvent *event)
133{
134 QFrame::paintEvent(event);
135
136 QPainter painter(this);
137 QRect rect = contentsRect();
138//! [7]
139
140 if (isPaused) {
141 painter.drawText(r: rect, flags: Qt::AlignCenter, text: tr(s: "Pause"));
142 return;
143 }
144
145//! [8]
146 int boardTop = rect.bottom() - BoardHeight*squareHeight();
147
148 for (int i = 0; i < BoardHeight; ++i) {
149 for (int j = 0; j < BoardWidth; ++j) {
150 TetrixShape shape = shapeAt(x: j, y: BoardHeight - i - 1);
151 if (shape != NoShape)
152 drawSquare(painter, x: rect.left() + j * squareWidth(),
153 y: boardTop + i * squareHeight(), shape);
154 }
155//! [8] //! [9]
156 }
157//! [9]
158
159//! [10]
160 if (curPiece.shape() != NoShape) {
161 for (int i = 0; i < 4; ++i) {
162 int x = curX + curPiece.x(index: i);
163 int y = curY - curPiece.y(index: i);
164 drawSquare(painter, x: rect.left() + x * squareWidth(),
165 y: boardTop + (BoardHeight - y - 1) * squareHeight(),
166 shape: curPiece.shape());
167 }
168//! [10] //! [11]
169 }
170//! [11] //! [12]
171}
172//! [12]
173
174//! [13]
175void TetrixBoard::keyPressEvent(QKeyEvent *event)
176{
177 if (!isStarted || isPaused || curPiece.shape() == NoShape) {
178 QFrame::keyPressEvent(event);
179 return;
180 }
181//! [13]
182
183//! [14]
184 switch (event->key()) {
185 case Qt::Key_Left:
186 tryMove(newPiece: curPiece, newX: curX - 1, newY: curY);
187 break;
188 case Qt::Key_Right:
189 tryMove(newPiece: curPiece, newX: curX + 1, newY: curY);
190 break;
191 case Qt::Key_Down:
192 tryMove(newPiece: curPiece.rotatedRight(), newX: curX, newY: curY);
193 break;
194 case Qt::Key_Up:
195 tryMove(newPiece: curPiece.rotatedLeft(), newX: curX, newY: curY);
196 break;
197 case Qt::Key_Space:
198 dropDown();
199 break;
200 case Qt::Key_D:
201 oneLineDown();
202 break;
203 default:
204 QFrame::keyPressEvent(event);
205 }
206//! [14]
207}
208
209//! [15]
210void TetrixBoard::timerEvent(QTimerEvent *event)
211{
212 if (event->timerId() == timer.timerId()) {
213 if (isWaitingAfterLine) {
214 isWaitingAfterLine = false;
215 newPiece();
216 timer.start(msec: timeoutTime(), obj: this);
217 } else {
218 oneLineDown();
219 }
220 } else {
221 QFrame::timerEvent(event);
222//! [15] //! [16]
223 }
224//! [16] //! [17]
225}
226//! [17]
227
228//! [18]
229void TetrixBoard::clearBoard()
230{
231 for (int i = 0; i < BoardHeight * BoardWidth; ++i)
232 board[i] = NoShape;
233}
234//! [18]
235
236//! [19]
237void TetrixBoard::dropDown()
238{
239 int dropHeight = 0;
240 int newY = curY;
241 while (newY > 0) {
242 if (!tryMove(newPiece: curPiece, newX: curX, newY: newY - 1))
243 break;
244 --newY;
245 ++dropHeight;
246 }
247 pieceDropped(dropHeight);
248//! [19] //! [20]
249}
250//! [20]
251
252//! [21]
253void TetrixBoard::oneLineDown()
254{
255 if (!tryMove(newPiece: curPiece, newX: curX, newY: curY - 1))
256 pieceDropped(dropHeight: 0);
257}
258//! [21]
259
260//! [22]
261void TetrixBoard::pieceDropped(int dropHeight)
262{
263 for (int i = 0; i < 4; ++i) {
264 int x = curX + curPiece.x(index: i);
265 int y = curY - curPiece.y(index: i);
266 shapeAt(x, y) = curPiece.shape();
267 }
268
269 ++numPiecesDropped;
270 if (numPiecesDropped % 25 == 0) {
271 ++level;
272 timer.start(msec: timeoutTime(), obj: this);
273 emit levelChanged(level);
274 }
275
276 score += dropHeight + 7;
277 emit scoreChanged(score);
278 removeFullLines();
279
280 if (!isWaitingAfterLine)
281 newPiece();
282//! [22] //! [23]
283}
284//! [23]
285
286//! [24]
287void TetrixBoard::removeFullLines()
288{
289 int numFullLines = 0;
290
291 for (int i = BoardHeight - 1; i >= 0; --i) {
292 bool lineIsFull = true;
293
294 for (int j = 0; j < BoardWidth; ++j) {
295 if (shapeAt(x: j, y: i) == NoShape) {
296 lineIsFull = false;
297 break;
298 }
299 }
300
301 if (lineIsFull) {
302//! [24] //! [25]
303 ++numFullLines;
304 for (int k = i; k < BoardHeight - 1; ++k) {
305 for (int j = 0; j < BoardWidth; ++j)
306 shapeAt(x: j, y: k) = shapeAt(x: j, y: k + 1);
307 }
308//! [25] //! [26]
309 for (int j = 0; j < BoardWidth; ++j)
310 shapeAt(x: j, y: BoardHeight - 1) = NoShape;
311 }
312//! [26] //! [27]
313 }
314//! [27]
315
316//! [28]
317 if (numFullLines > 0) {
318 numLinesRemoved += numFullLines;
319 score += 10 * numFullLines;
320 emit linesRemovedChanged(numLines: numLinesRemoved);
321 emit scoreChanged(score);
322
323 timer.start(msec: 500, obj: this);
324 isWaitingAfterLine = true;
325 curPiece.setShape(NoShape);
326 update();
327 }
328//! [28] //! [29]
329}
330//! [29]
331
332//! [30]
333void TetrixBoard::newPiece()
334{
335 curPiece = nextPiece;
336 nextPiece.setRandomShape();
337 showNextPiece();
338 curX = BoardWidth / 2 + 1;
339 curY = BoardHeight - 1 + curPiece.minY();
340
341 if (!tryMove(newPiece: curPiece, newX: curX, newY: curY)) {
342 curPiece.setShape(NoShape);
343 timer.stop();
344 isStarted = false;
345 }
346//! [30] //! [31]
347}
348//! [31]
349
350//! [32]
351void TetrixBoard::showNextPiece()
352{
353 if (!nextPieceLabel)
354 return;
355
356 int dx = nextPiece.maxX() - nextPiece.minX() + 1;
357 int dy = nextPiece.maxY() - nextPiece.minY() + 1;
358
359 QPixmap pixmap(dx * squareWidth(), dy * squareHeight());
360 QPainter painter(&pixmap);
361 painter.fillRect(pixmap.rect(), nextPieceLabel->palette().window());
362
363 for (int i = 0; i < 4; ++i) {
364 int x = nextPiece.x(index: i) - nextPiece.minX();
365 int y = nextPiece.y(index: i) - nextPiece.minY();
366 drawSquare(painter, x: x * squareWidth(), y: y * squareHeight(),
367 shape: nextPiece.shape());
368 }
369 nextPieceLabel->setPixmap(pixmap);
370//! [32] //! [33]
371}
372//! [33]
373
374//! [34]
375bool TetrixBoard::tryMove(const TetrixPiece &newPiece, int newX, int newY)
376{
377 for (int i = 0; i < 4; ++i) {
378 int x = newX + newPiece.x(index: i);
379 int y = newY - newPiece.y(index: i);
380 if (x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight)
381 return false;
382 if (shapeAt(x, y) != NoShape)
383 return false;
384 }
385//! [34]
386
387//! [35]
388 curPiece = newPiece;
389 curX = newX;
390 curY = newY;
391 update();
392 return true;
393}
394//! [35]
395
396//! [36]
397void TetrixBoard::drawSquare(QPainter &painter, int x, int y, TetrixShape shape)
398{
399 static constexpr QRgb colorTable[8] = {
400 0x000000, 0xCC6666, 0x66CC66, 0x6666CC,
401 0xCCCC66, 0xCC66CC, 0x66CCCC, 0xDAAA00
402 };
403
404 QColor color = colorTable[int(shape)];
405 painter.fillRect(x: x + 1, y: y + 1, w: squareWidth() - 2, h: squareHeight() - 2,
406 b: color);
407
408 painter.setPen(color.lighter());
409 painter.drawLine(x1: x, y1: y + squareHeight() - 1, x2: x, y2: y);
410 painter.drawLine(x1: x, y1: y, x2: x + squareWidth() - 1, y2: y);
411
412 painter.setPen(color.darker());
413 painter.drawLine(x1: x + 1, y1: y + squareHeight() - 1,
414 x2: x + squareWidth() - 1, y2: y + squareHeight() - 1);
415 painter.drawLine(x1: x + squareWidth() - 1, y1: y + squareHeight() - 1,
416 x2: x + squareWidth() - 1, y2: y + 1);
417}
418//! [36]
419

source code of qtbase/examples/widgets/widgets/tetrix/tetrixboard.cpp