summaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
authorNicolas Ettlin <nicolas.ettlin@me.com>2018-07-16 13:14:20 +0200
committerhjk <hjk@qt.io>2018-08-09 08:09:14 +0000
commit77b4923c999fb0149e440500096662cbb7c8a4c8 (patch)
treebcf99b3d3e9e5f4c310b8d155c8ef7580e2e60c7 /examples
parent024cb75206c0be123729c1fd1ddd99b634e0350f (diff)
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 <hjk@qt.io> Reviewed-by: Paul Wicking <paul.wicking@qt.io> Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
Diffstat (limited to 'examples')
-rw-r--r--examples/quick/tableview/gameoflife/doc/images/gameoflife.pngbin0 -> 47006 bytes
-rw-r--r--examples/quick/tableview/gameoflife/doc/src/gameoflife.qdoc113
-rw-r--r--examples/quick/tableview/gameoflife/gameoflife.pro16
-rw-r--r--examples/quick/tableview/gameoflife/gameoflifemodel.cpp214
-rw-r--r--examples/quick/tableview/gameoflife/gameoflifemodel.h106
-rw-r--r--examples/quick/tableview/gameoflife/gosperglidergun.cells9
-rw-r--r--examples/quick/tableview/gameoflife/main.cpp70
-rw-r--r--examples/quick/tableview/gameoflife/main.qml157
-rw-r--r--examples/quick/tableview/tableview.pro4
9 files changed, 688 insertions, 1 deletions
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
--- /dev/null
+++ b/examples/quick/tableview/gameoflife/doc/images/gameoflife.png
Binary files 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 <QFile>
+#include <QTextStream>
+#include <QRect>
+
+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<int>(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 <array>
+#include <QAbstractTableModel>
+#include <QPoint>
+
+//! [modelclass]
+class GameOfLifeModel : public QAbstractTableModel
+{
+ Q_OBJECT
+
+ Q_ENUMS(Roles)
+public:
+ enum Roles {
+ CellRole
+ };
+
+ QHash<int, QByteArray> 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<bool, size>;
+ 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 <QGuiApplication>
+#include <QQmlApplicationEngine>
+#include <gameoflifemodel.h>
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ QGuiApplication app(argc, argv);
+ QQmlApplicationEngine engine;
+
+ //! [registertype]
+ qmlRegisterType<GameOfLifeModel>("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");
+ }
+}
diff --git a/examples/quick/tableview/tableview.pro b/examples/quick/tableview/tableview.pro
index 2bcd119c31..4177ef91fb 100644
--- a/examples/quick/tableview/tableview.pro
+++ b/examples/quick/tableview/tableview.pro
@@ -1,3 +1,5 @@
TEMPLATE = subdirs
-SUBDIRS += pixelator
+SUBDIRS += \
+ gameoflife \
+ pixelator