From 77b4923c999fb0149e440500096662cbb7c8a4c8 Mon Sep 17 00:00:00 2001 From: Nicolas Ettlin Date: Mon, 16 Jul 2018 13:14:20 +0200 Subject: TableView Conway's Game of Life example This new example shows how a Conway's Game of Life implementation can be created with QML and the new TableView component. Change-Id: I940210c5e5a0554e6f052ff109070e69e59cab56 Reviewed-by: hjk Reviewed-by: Paul Wicking Reviewed-by: Richard Moe Gustavsen --- .../tableview/gameoflife/doc/images/gameoflife.png | Bin 0 -> 47006 bytes .../tableview/gameoflife/doc/src/gameoflife.qdoc | 113 +++++++++++ examples/quick/tableview/gameoflife/gameoflife.pro | 16 ++ .../quick/tableview/gameoflife/gameoflifemodel.cpp | 214 +++++++++++++++++++++ .../quick/tableview/gameoflife/gameoflifemodel.h | 106 ++++++++++ .../tableview/gameoflife/gosperglidergun.cells | 9 + examples/quick/tableview/gameoflife/main.cpp | 70 +++++++ examples/quick/tableview/gameoflife/main.qml | 157 +++++++++++++++ 8 files changed, 685 insertions(+) create mode 100644 examples/quick/tableview/gameoflife/doc/images/gameoflife.png create mode 100644 examples/quick/tableview/gameoflife/doc/src/gameoflife.qdoc create mode 100644 examples/quick/tableview/gameoflife/gameoflife.pro create mode 100644 examples/quick/tableview/gameoflife/gameoflifemodel.cpp create mode 100644 examples/quick/tableview/gameoflife/gameoflifemodel.h create mode 100644 examples/quick/tableview/gameoflife/gosperglidergun.cells create mode 100644 examples/quick/tableview/gameoflife/main.cpp create mode 100644 examples/quick/tableview/gameoflife/main.qml (limited to 'examples/quick/tableview/gameoflife') diff --git a/examples/quick/tableview/gameoflife/doc/images/gameoflife.png b/examples/quick/tableview/gameoflife/doc/images/gameoflife.png new file mode 100644 index 0000000000..252dda2e11 Binary files /dev/null and b/examples/quick/tableview/gameoflife/doc/images/gameoflife.png differ diff --git a/examples/quick/tableview/gameoflife/doc/src/gameoflife.qdoc b/examples/quick/tableview/gameoflife/doc/src/gameoflife.qdoc new file mode 100644 index 0000000000..b2391d43ba --- /dev/null +++ b/examples/quick/tableview/gameoflife/doc/src/gameoflife.qdoc @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** 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. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \title Qt Quick TableView examples - Conway’s Game of Life + \example gameoflife + \brief The \e{Conway’s Game of Life} example shows how the QML TableView + type can be used to display a C++ model that the user can pan around. + + \image gameoflife.png + \ingroup qtquickexamples + + \include examples-run.qdocinc + + \section1 The QML User Interface + + \snippet tableview/gameoflife/main.qml tableview + The example uses the TableView component to display a grid of cells. Each + of these cells is drawn on the screen by the TableView’s delegate, which is + a Rectangle QML component. We read the cell’s value and we change it + using \c{model.value} when the user clicks it. + + \snippet tableview/gameoflife/main.qml scroll + When the application starts, the TableView is scrolled to its center + by using its \c{contentX} and \c{contentY} properties to update the scroll + position, and the \c{contentWidth} and \c{contentHeight} to compute where + the view should be scrolled to. + + \snippet tableview/gameoflife/main.qml model + The model that we use is a custom C++ class that we register + in the QML system: + \snippet tableview/gameoflife/main.cpp registertype + + \section1 The C++ Model + + \snippet tableview/gameoflife/gameoflifemodel.h modelclass + The \c{GameOfLifeModel} class extends QAbstractTableModel so it can be + used as the model of our TableView component. Therefore, it needs to + implement some functions so the TableView component can interact with + the model. As you can see in the \c private part of the class, the model + uses a fixed-size array to store the current state of all the cells. + + \snippet tableview/gameoflife/gameoflifemodel.cpp modelsize + Here, the \c rowCount and \c columnCount methods are implemented so + the TableView component can know the size of the table. It simply returns + the values of the \c width and \c height constants. + + \snippet tableview/gameoflife/gameoflifemodel.cpp read + This method is called when the TableView component requests some data from + the model. In our example, we only have one piece of data by cell: whether + it is alive or not. This information is represented by the \c CellRole value + of the \c Roles enum in our C++ code; this corresponds to the \c value + property in the QML code (the link between these two is made by the + \c{roleNames()} function of our C++ class). + + The \c GameOfLifeModel class can identify which cell was the data requested + from with the \c index parameter, which is a QModelIndex that contains + a row and a column. + + \section1 Updating the Data + + \snippet tableview/gameoflife/gameoflifemodel.cpp write + The \c setData method is called when a property’s value is set from the + QML interface: in our example, it toggles a cell’s state when it is clicked. + In the same way as the \c{data()} function does, this method receives an + \c index and a \c role parameter. Additionally, the new value is passed + as a QVariant, that we convert to a boolean using the \c toBool function. + + When we update the internal state of our model object, we need to emit a + \c dataChanged signal to tell the TableView component that it needs to update the + displayed data. In this case, only the cell that was clicked is affected, thus + the range of the table that has to be updated begins and ends at the cell’s index. + + \snippet tableview/gameoflife/gameoflifemodel.cpp update + This function can be called directly from the QML code, because it contains the + Q_INVOKABLE macro in its definition. It plays an iteration of the game, either when + the user clicks the \e{Next} button or when the Timer emits a \c{triggered()} signal. + + Following the \e{Conway’s Game of Life} rules, a new state is computed for each + cell depending on the current state of its neighbors. When the new state has + been computed for the whole grid, it replaces the current state and a + \e dataChanged signal is emitted for the whole table. + + \snippet tableview/gameoflife/gameoflifemodel.cpp loader + When the application opens, a pattern is loaded to demonstrate how + \e{Conway’s Game of Life} works. These two functions load the file where + the pattern is stored and parse it. As in the \c nextStep function, a + \c dataChanged signal is emitted for the whole table once the pattern + has been fully loaded. +*/ diff --git a/examples/quick/tableview/gameoflife/gameoflife.pro b/examples/quick/tableview/gameoflife/gameoflife.pro new file mode 100644 index 0000000000..98050b0d79 --- /dev/null +++ b/examples/quick/tableview/gameoflife/gameoflife.pro @@ -0,0 +1,16 @@ +TEMPLATE = app + +QT += quick qml +SOURCES += \ + main.cpp \ + gameoflifemodel.cpp + +RESOURCES += \ + main.qml \ + gosperglidergun.cells + +target.path = $$[QT_INSTALL_EXAMPLES]/quick/tableview/gameoflife +INSTALLS += target + +HEADERS += \ + gameoflifemodel.h diff --git a/examples/quick/tableview/gameoflife/gameoflifemodel.cpp b/examples/quick/tableview/gameoflife/gameoflifemodel.cpp new file mode 100644 index 0000000000..32765f87a8 --- /dev/null +++ b/examples/quick/tableview/gameoflife/gameoflifemodel.cpp @@ -0,0 +1,214 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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 "gameoflifemodel.h" +#include +#include +#include + +GameOfLifeModel::GameOfLifeModel(QObject *parent) + : QAbstractTableModel(parent) +{ + clear(); +} + +//! [modelsize] +int GameOfLifeModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return height; +} + +int GameOfLifeModel::columnCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return width; +} +//! [modelsize] + +//! [read] +QVariant GameOfLifeModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || role != CellRole) + return QVariant(); + + return QVariant(m_currentState[cellIndex({index.column(), index.row()})]); +} +//! [read] + +//! [write] +bool GameOfLifeModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (role != CellRole || data(index, role) == value) + return false; + + m_currentState[cellIndex({index.column(), index.row()})] = value.toBool(); + emit dataChanged(index, index, {role}); + + return true; +} +//! [write] + +Qt::ItemFlags GameOfLifeModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + + return Qt::ItemIsEditable; +} + +//! [update] +void GameOfLifeModel::nextStep() +{ + StateContainer newValues; + + for (std::size_t i = 0; i < size; ++i) { + bool currentState = m_currentState[i]; + + int cellNeighborsCount = this->cellNeighborsCount(cellCoordinatesFromIndex(static_cast(i))); + + newValues[i] = currentState == true + ? cellNeighborsCount == 2 || cellNeighborsCount == 3 + : cellNeighborsCount == 3; + } + + m_currentState = std::move(newValues); + + emit dataChanged(index(0, 0), index(height - 1, width - 1), {CellRole}); +} +//! [update] + +//! [loader] +bool GameOfLifeModel::loadFile(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly)) + return false; + + QTextStream in(&file); + loadPattern(in.readAll()); + + return true; +} + +void GameOfLifeModel::loadPattern(const QString &plainText) +{ + clear(); + + QStringList rows = plainText.split("\n"); + QSize patternSize(0, rows.count()); + for (QString row : rows) { + if (row.size() > patternSize.width()) + patternSize.setWidth(row.size()); + } + + QPoint patternLocation((width - patternSize.width()) / 2, (height - patternSize.height()) / 2); + + for (int y = 0; y < patternSize.height(); ++y) { + const QString line = rows[y]; + + for (int x = 0; x < line.length(); ++x) { + QPoint cellPosition(x + patternLocation.x(), y + patternLocation.y()); + m_currentState[cellIndex(cellPosition)] = line[x] == 'O'; + } + } + + emit dataChanged(index(0, 0), index(height - 1, width - 1), {CellRole}); +} +//! [loader] + +void GameOfLifeModel::clear() +{ + m_currentState.fill(false); + emit dataChanged(index(0, 0), index(height - 1, width - 1), {CellRole}); +} + +int GameOfLifeModel::cellNeighborsCount(const QPoint &cellCoordinates) const +{ + int count = 0; + + for (int x = -1; x <= 1; ++x) { + for (int y = -1; y <= 1; ++y) { + if (x == 0 && y == 0) + continue; + + const QPoint neighborPosition { cellCoordinates.x() + x, cellCoordinates.y() + y }; + if (!areCellCoordinatesValid(neighborPosition)) + continue; + + if (m_currentState[cellIndex(neighborPosition)]) + ++count; + + if (count > 3) + return count; + } + } + + return count; +} + +bool GameOfLifeModel::areCellCoordinatesValid(const QPoint &coordinates) +{ + return QRect(0, 0, width, height).contains(coordinates); +} + +QPoint GameOfLifeModel::cellCoordinatesFromIndex(int cellIndex) +{ + return {cellIndex % width, cellIndex / width}; +} + +std::size_t GameOfLifeModel::cellIndex(const QPoint &coordinates) +{ + return std::size_t(coordinates.y() * width + coordinates.x()); +} diff --git a/examples/quick/tableview/gameoflife/gameoflifemodel.h b/examples/quick/tableview/gameoflife/gameoflifemodel.h new file mode 100644 index 0000000000..3ea1469861 --- /dev/null +++ b/examples/quick/tableview/gameoflife/gameoflifemodel.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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$ +** +****************************************************************************/ + +#ifndef GAMEOFLIFEMODEL_H +#define GAMEOFLIFEMODEL_H + +#include +#include +#include + +//! [modelclass] +class GameOfLifeModel : public QAbstractTableModel +{ + Q_OBJECT + + Q_ENUMS(Roles) +public: + enum Roles { + CellRole + }; + + QHash roleNames() const override { + return { + { CellRole, "value" } + }; + } + + explicit GameOfLifeModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole) override; + + Qt::ItemFlags flags(const QModelIndex &index) const override; + + Q_INVOKABLE void nextStep(); + Q_INVOKABLE bool loadFile(const QString &fileName); + Q_INVOKABLE void loadPattern(const QString &plainText); + Q_INVOKABLE void clear(); + +private: + static constexpr int width = 256; + static constexpr int height = 256; + static constexpr int size = width * height; + + using StateContainer = std::array; + StateContainer m_currentState; + + int cellNeighborsCount(const QPoint &cellCoordinates) const; + static bool areCellCoordinatesValid(const QPoint &coordinates); + static QPoint cellCoordinatesFromIndex(int cellIndex); + static std::size_t cellIndex(const QPoint &coordinates); +}; +//! [modelclass] + +#endif // GAMEOFLIFEMODEL_H diff --git a/examples/quick/tableview/gameoflife/gosperglidergun.cells b/examples/quick/tableview/gameoflife/gosperglidergun.cells new file mode 100644 index 0000000000..50aaadf3d5 --- /dev/null +++ b/examples/quick/tableview/gameoflife/gosperglidergun.cells @@ -0,0 +1,9 @@ +........................O +......................O.O +............OO......OO............OO +...........O...O....OO............OO +OO........O.....O...OO +OO........O...O.OO....O.O +..........O.....O.......O +...........O...O +............OO diff --git a/examples/quick/tableview/gameoflife/main.cpp b/examples/quick/tableview/gameoflife/main.cpp new file mode 100644 index 0000000000..5101880b06 --- /dev/null +++ b/examples/quick/tableview/gameoflife/main.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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 +#include +#include + +int main(int argc, char *argv[]) +{ + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QGuiApplication app(argc, argv); + QQmlApplicationEngine engine; + + //! [registertype] + qmlRegisterType("GameOfLifeModel", 1, 0, "GameOfLifeModel"); + //! [registertype] + + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + if (engine.rootObjects().isEmpty()) + return -1; + + return app.exec(); +} diff --git a/examples/quick/tableview/gameoflife/main.qml b/examples/quick/tableview/gameoflife/main.qml new file mode 100644 index 0000000000..c25f7e9a18 --- /dev/null +++ b/examples/quick/tableview/gameoflife/main.qml @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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$ +** +****************************************************************************/ + +import QtQuick 2.9 +import QtQuick.Window 2.3 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import Qt.labs.tableview 1.0 +import GameOfLifeModel 1.0 + +ApplicationWindow { + id: root + visible: true + width: 760 + height: 810 + minimumWidth: 475 + minimumHeight: 300 + + color: "#09102B" + title: qsTr("Conway’s Game of Life") + + //! [tableview] + TableView { + id: tableView + anchors.fill: parent + + rowSpacing: 1 + columnSpacing: 1 + + ScrollBar.horizontal: ScrollBar {} + ScrollBar.vertical: ScrollBar {} + + delegate: Rectangle { + id: cell + implicitWidth: 15 + implicitHeight: 15 + + color: model.value ? "#f3f3f4" : "#b5b7bf" + + MouseArea { + anchors.fill: parent + onClicked: model.value = !model.value + } + } + //! [tableview] + + //! [model] + model: GameOfLifeModel { + id: gameOfLifeModel + } + //! [model] + + //! [scroll] + Component.onCompleted: { + tableView.contentX = (tableView.contentWidth - tableView.width) / 2; + tableView.contentY = (tableView.contentHeight - tableView.height) / 2; + } + //! [scroll] + } + + footer: Rectangle { + signal nextStep + + id: footer + height: 50 + color: "#F3F3F4" + + RowLayout { + anchors.centerIn: parent + + //! [next] + Button { + text: qsTr("Next") + onClicked: gameOfLifeModel.nextStep() + } + //! [next] + + Item { + width: 50 + } + + Slider { + id: slider + x: 245 + y: 17 + from: 0 + to: 1 + value: 0.9 + } + + Button { + text: timer.running ? "❙❙" : "▶️" + onClicked: timer.running = !timer.running + } + } + + Timer { + id: timer + interval: 1000 - (980 * slider.value) + running: true + repeat: true + + onTriggered: gameOfLifeModel.nextStep() + } + } + + Component.onCompleted: { + gameOfLifeModel.loadFile(":/gosperglidergun.cells"); + } +} -- cgit v1.2.3