- Qt 5.5
- Qt Widgets
- Tetrix Example
- tetrixboard.cpp Example File
- Qt 5.5.1 Reference Documentation

# tetrixboard.cpp Example File

widgets/tetrix/tetrixboard.cpp

    /****************************************************************************
    **
    ** Copyright (C) 2015 The Qt Company Ltd.
    ** Contact: http://www.qt.io/licensing/
    **
    ** This file is part of the examples of the Qt Toolkit.
    **
    ** $QT_BEGIN_LICENSE:BSD$
    ** You may use this file under the terms of the BSD license as follows:
    **
    ** "Redistribution and use in source and binary forms, with or without
    ** modification, are permitted provided that the following conditions are
    ** met:
    **   * Redistributions of source code must retain the above copyright
    **     notice, this list of conditions and the following disclaimer.
    **   * Redistributions in binary form must reproduce the above copyright
    **     notice, this list of conditions and the following disclaimer in
    **     the documentation and/or other materials provided with the
    **     distribution.
    **   * Neither the name of The Qt Company Ltd nor the names of its
    **     contributors may be used to endorse or promote products derived
    **     from this software without specific prior written permission.
    **
    **
    ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
    **
    ** $QT_END_LICENSE$
    **
    ****************************************************************************/

    #include <QtWidgets>

    #include "tetrixboard.h"

    TetrixBoard::TetrixBoard(QWidget *parent)
        : QFrame(parent)
    {
        setFrameStyle(QFrame::Panel | QFrame::Sunken);
        setFocusPolicy(Qt::StrongFocus);
        isStarted = false;
        isPaused = false;
        clearBoard();

        nextPiece.setRandomShape();
    }

    void TetrixBoard::setNextPieceLabel(QLabel *label)
    {
        nextPieceLabel = label;
    }

    QSize TetrixBoard::sizeHint() const
    {
        return QSize(BoardWidth * 15 + frameWidth() * 2,
                     BoardHeight * 15 + frameWidth() * 2);
    }

    QSize TetrixBoard::minimumSizeHint() const
    {
        return QSize(BoardWidth * 5 + frameWidth() * 2,
                     BoardHeight * 5 + frameWidth() * 2);
    }

    void TetrixBoard::start()
    {
        if (isPaused)
            return;

        isStarted = true;
        isWaitingAfterLine = false;
        numLinesRemoved = 0;
        numPiecesDropped = 0;
        score = 0;
        level = 1;
        clearBoard();

        emit linesRemovedChanged(numLinesRemoved);
        emit scoreChanged(score);
        emit levelChanged(level);

        newPiece();
        timer.start(timeoutTime(), this);
    }

    void TetrixBoard::pause()
    {
        if (!isStarted)
            return;

        isPaused = !isPaused;
        if (isPaused) {
            timer.stop();
        } else {
            timer.start(timeoutTime(), this);
        }
        update();
    }

    void TetrixBoard::paintEvent(QPaintEvent *event)
    {
        QFrame::paintEvent(event);

        QPainter painter(this);
        QRect rect = contentsRect();

        if (isPaused) {
            painter.drawText(rect, Qt::AlignCenter, tr("Pause"));
            return;
        }

        int boardTop = rect.bottom() - BoardHeight*squareHeight();

        for (int i = 0; i < BoardHeight; ++i) {
            for (int j = 0; j < BoardWidth; ++j) {
                TetrixShape shape = shapeAt(j, BoardHeight - i - 1);
                if (shape != NoShape)
                    drawSquare(painter, rect.left() + j * squareWidth(),
                               boardTop + i * squareHeight(), shape);
            }
        }

        if (curPiece.shape() != NoShape) {
            for (int i = 0; i < 4; ++i) {
                int x = curX + curPiece.x(i);
                int y = curY - curPiece.y(i);
                drawSquare(painter, rect.left() + x * squareWidth(),
                           boardTop + (BoardHeight - y - 1) * squareHeight(),
                           curPiece.shape());
            }
        }
    }

    void TetrixBoard::keyPressEvent(QKeyEvent *event)
    {
        if (!isStarted || isPaused || curPiece.shape() == NoShape) {
            QFrame::keyPressEvent(event);
            return;
        }

        switch (event->key()) {
        case Qt::Key_Left:
            tryMove(curPiece, curX - 1, curY);
            break;
        case Qt::Key_Right:
            tryMove(curPiece, curX + 1, curY);
            break;
        case Qt::Key_Down:
            tryMove(curPiece.rotatedRight(), curX, curY);
            break;
        case Qt::Key_Up:
            tryMove(curPiece.rotatedLeft(), curX, curY);
            break;
        case Qt::Key_Space:
            dropDown();
            break;
        case Qt::Key_D:
            oneLineDown();
            break;
        default:
            QFrame::keyPressEvent(event);
        }
    }

    void TetrixBoard::timerEvent(QTimerEvent *event)
    {
        if (event->timerId() == timer.timerId()) {
            if (isWaitingAfterLine) {
                isWaitingAfterLine = false;
                newPiece();
                timer.start(timeoutTime(), this);
            } else {
                oneLineDown();
            }
        } else {
            QFrame::timerEvent(event);
        }
    }

    void TetrixBoard::clearBoard()
    {
        for (int i = 0; i < BoardHeight * BoardWidth; ++i)
            board[i] = NoShape;
    }

    void TetrixBoard::dropDown()
    {
        int dropHeight = 0;
        int newY = curY;
        while (newY > 0) {
            if (!tryMove(curPiece, curX, newY - 1))
                break;
            --newY;
            ++dropHeight;
        }
        pieceDropped(dropHeight);
    }

    void TetrixBoard::oneLineDown()
    {
        if (!tryMove(curPiece, curX, curY - 1))
            pieceDropped(0);
    }

    void TetrixBoard::pieceDropped(int dropHeight)
    {
        for (int i = 0; i < 4; ++i) {
            int x = curX + curPiece.x(i);
            int y = curY - curPiece.y(i);
            shapeAt(x, y) = curPiece.shape();
        }

        ++numPiecesDropped;
        if (numPiecesDropped % 25 == 0) {
            ++level;
            timer.start(timeoutTime(), this);
            emit levelChanged(level);
        }

        score += dropHeight + 7;
        emit scoreChanged(score);
        removeFullLines();

        if (!isWaitingAfterLine)
            newPiece();
    }

    void TetrixBoard::removeFullLines()
    {
        int numFullLines = 0;

        for (int i = BoardHeight - 1; i >= 0; --i) {
            bool lineIsFull = true;

            for (int j = 0; j < BoardWidth; ++j) {
                if (shapeAt(j, i) == NoShape) {
                    lineIsFull = false;
                    break;
                }
            }

            if (lineIsFull) {
                ++numFullLines;
                for (int k = i; k < BoardHeight - 1; ++k) {
                    for (int j = 0; j < BoardWidth; ++j)
                        shapeAt(j, k) = shapeAt(j, k + 1);
                }
                for (int j = 0; j < BoardWidth; ++j)
                    shapeAt(j, BoardHeight - 1) = NoShape;
            }
        }

        if (numFullLines > 0) {
            numLinesRemoved += numFullLines;
            score += 10 * numFullLines;
            emit linesRemovedChanged(numLinesRemoved);
            emit scoreChanged(score);

            timer.start(500, this);
            isWaitingAfterLine = true;
            curPiece.setShape(NoShape);
            update();
        }
    }

    void TetrixBoard::newPiece()
    {
        curPiece = nextPiece;
        nextPiece.setRandomShape();
        showNextPiece();
        curX = BoardWidth / 2 + 1;
        curY = BoardHeight - 1 + curPiece.minY();

        if (!tryMove(curPiece, curX, curY)) {
            curPiece.setShape(NoShape);
            timer.stop();
            isStarted = false;
        }
    }

    void TetrixBoard::showNextPiece()
    {
        if (!nextPieceLabel)
            return;

        int dx = nextPiece.maxX() - nextPiece.minX() + 1;
        int dy = nextPiece.maxY() - nextPiece.minY() + 1;

        QPixmap pixmap(dx * squareWidth(), dy * squareHeight());
        QPainter painter(&pixmap);
        painter.fillRect(pixmap.rect(), nextPieceLabel->palette().background());

        for (int i = 0; i < 4; ++i) {
            int x = nextPiece.x(i) - nextPiece.minX();
            int y = nextPiece.y(i) - nextPiece.minY();
            drawSquare(painter, x * squareWidth(), y * squareHeight(),
                       nextPiece.shape());
        }
        nextPieceLabel->setPixmap(pixmap);
    }

    bool TetrixBoard::tryMove(const TetrixPiece &newPiece, int newX, int newY)
    {
        for (int i = 0; i < 4; ++i) {
            int x = newX + newPiece.x(i);
            int y = newY - newPiece.y(i);
            if (x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight)
                return false;
            if (shapeAt(x, y) != NoShape)
                return false;
        }

        curPiece = newPiece;
        curX = newX;
        curY = newY;
        update();
        return true;
    }

    void TetrixBoard::drawSquare(QPainter &painter, int x, int y, TetrixShape shape)
    {
        static const QRgb colorTable[8] = {
            0x000000, 0xCC6666, 0x66CC66, 0x6666CC,
            0xCCCC66, 0xCC66CC, 0x66CCCC, 0xDAAA00
        };

        QColor color = colorTable[int(shape)];
        painter.fillRect(x + 1, y + 1, squareWidth() - 2, squareHeight() - 2,
                         color);

        painter.setPen(color.light());
        painter.drawLine(x, y + squareHeight() - 1, x, y);
        painter.drawLine(x, y, x + squareWidth() - 1, y);

        painter.setPen(color.dark());
        painter.drawLine(x + 1, y + squareHeight() - 1,
                         x + squareWidth() - 1, y + squareHeight() - 1);
        painter.drawLine(x + squareWidth() - 1, y + squareHeight() - 1,
                         x + squareWidth() - 1, y + 1);
    }