I have a board game with numbered tiles positioned in a square grid. The board uses GridView with custom GameBoard model (GameBoardModel as exported to QML) inherited from QAbstractListModel. The game in it’s standard form uses a 4×4 tile grid because that’s how many (16) tiles the default constructor of the model creates. However I thought of making some sort of a combobox that would allow choosing the board size, which will then be generated and the game begins.
Now the problem is that I don’t see an obvious way to send a number (or call a function) from QML which will proceed to generate a model with desired parameters, load that into QML and update the view.
I’ve tried creating a separate button and using the dimension property of my model to set dimension to a new value via MouseArea‘s onClicked and writing _gameboard.model.dimension = 5 (where _gameboard is the id of GameBoard QML type which uses GameBoardModel), but I still couldn’t figure out how to properly update my model. I’ve tried creating some sort of reloadModel method which essentially creates a new list of tiles with new numbers and that would be called after every update of dimension, but even after calling dataChanged it only displayed the old number of elements which is 16 and not 25 (not counting one intentionally hidden).
C++
part of gameboard.h
class GameBoard : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(int dimension READ dimension WRITE setDimension NOTIFY dimensionChanged)
Q_PROPERTY(int hiddenElementValue READ boardSize NOTIFY hiddenElementChanged)
public:
static constexpr size_t defaultPuzzleDimension = 4;
GameBoard(QObject* parent = nullptr, const size_t boardDimension = defaultPuzzleDimension);
size_t dimension() const;
void setDimension(size_t newDimension);
size_t boardSize() const;
signals:
void dimensionChanged(const size_t newDimension);
void hiddenElementChanged(int newHiddenElementValue);
private:
std::vector<Tile> m_rawBoard;
size_t m_dimension;
size_t m_boardSize;
void reloadModel();
}
part of gameboard.cpp
size_t GameBoard::dimension() const
{
return m_dimension;
}
void GameBoard::setDimension(size_t newDimension)
{
m_dimension = newDimension;
reloadModel();
emit dimensionChanged(m_dimension);
emit hiddenElementChanged(m_boardSize);
}
size_t GameBoard::boardSize() const
{
return m_boardSize;
}
void GameBoard::reloadModel()
{
m_rawBoard.clear();
m_boardSize = m_dimension * m_dimension;
m_rawBoard.resize(m_boardSize);
std::iota(m_rawBoard.begin(),m_rawBoard.end(),1);
shuffle();
dataChanged(createIndex(0,0),createIndex(m_boardSize,0));
}
QML
main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
id: root
width: 680
height: 440
visible: true
title: qsTr("Hello World")
Item {
id: _firstTile
width: 0.5 * root.width
height: 0.1 * root.height
anchors.horizontalCenter: parent.horizontalCenter
Tile {
radius: 30
anchors.fill: parent
anchors.topMargin: 0.05 * parent.height
anchors.bottomMargin: 0.05 * parent.height
MouseArea {
anchors.fill: parent
onClicked: {
_gameboard.model.dimension = 5;
}
}
}
}
Item {
width: root.width
height: 0.9 * root.height
anchors.top: _firstTile.bottom
GameBoard {
id: _gameboard
anchors.fill: parent
}
}
}
GameBoard.qml
import QtQuick 2.0
import Game 1.0
GridView {
id: root
model: GameBoardModel {
}
cellHeight: height / root.model.dimension
cellWidth: width / root.model.dimension
delegate: Item {
id: _backgroundDelegate
width: root.cellWidth
height: root.cellHeight
visible: display != root.model.hiddenElementValue
Tile {
displayText: display
anchors.fill: _backgroundDelegate
anchors.margins: 3
MouseArea {
anchors.fill: parent
onClicked: {
root.model.move(index);
}
}
}
}
}