Huge cleanups and added open gl window
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
#include "actionscontainermodel.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QIcon>
|
||||
|
||||
ActionsContainerModel::ActionsContainerModel(QObject *parent) :
|
||||
QAbstractListModel{parent}
|
||||
{
|
||||
}
|
||||
|
||||
int ActionsContainerModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return m_actionsContainer ? m_actionsContainer->size() : 0;
|
||||
}
|
||||
|
||||
QVariant ActionsContainerModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!m_actionsContainer)
|
||||
{
|
||||
qWarning() << "no currentEvent";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!index.isValid())
|
||||
{
|
||||
qWarning() << "unexpected invalid index";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (index.row() < 0 || std::size_t(index.row()) >= m_actionsContainer->size())
|
||||
{
|
||||
qWarning() << "unexpected row" << index.row();
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto &action = m_actionsContainer->at(index.row());
|
||||
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
return tr("Execute a piece of code");
|
||||
case Qt::DecorationRole:
|
||||
return QIcon{":/qtgameengine/icons/code-action.png"};
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void ActionsContainerModel::setActionsContainer(ActionsContainer *actionsContainer)
|
||||
{
|
||||
beginResetModel();
|
||||
m_actionsContainer = actionsContainer;
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
Action *ActionsContainerModel::getAction(const QModelIndex &index)
|
||||
{
|
||||
if (!index.isValid())
|
||||
{
|
||||
qWarning() << "unexpected invalid index";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return getAction(index.row());
|
||||
}
|
||||
|
||||
const Action *ActionsContainerModel::getAction(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
{
|
||||
qWarning() << "unexpected invalid index";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return getAction(index.row());
|
||||
}
|
||||
|
||||
Action *ActionsContainerModel::getAction(int row)
|
||||
{
|
||||
if (!m_actionsContainer)
|
||||
{
|
||||
qWarning() << "invalid actions container";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (row < 0 || std::size_t(row) >= m_actionsContainer->size())
|
||||
{
|
||||
qWarning() << "unexpected row" << row;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &*std::next(std::begin(*m_actionsContainer), row);
|
||||
}
|
||||
|
||||
const Action *ActionsContainerModel::getAction(int row) const
|
||||
{
|
||||
if (!m_actionsContainer)
|
||||
{
|
||||
qWarning() << "invalid actions container";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (row < 0 || std::size_t(row) >= m_actionsContainer->size())
|
||||
{
|
||||
qWarning() << "unexpected row" << row;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &*std::next(std::cbegin(*m_actionsContainer), row);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "projectcontainer.h"
|
||||
|
||||
class ActionsContainerModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ActionsContainerModel(QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
|
||||
ActionsContainer *actionsContainer() const { return m_actionsContainer; }
|
||||
void setActionsContainer(ActionsContainer *actionsContainer);
|
||||
|
||||
Action *getAction(const QModelIndex &index);
|
||||
const Action *getAction(const QModelIndex &index) const;
|
||||
Action *getAction(int row);
|
||||
const Action *getAction(int row) const;
|
||||
|
||||
private:
|
||||
ActionsContainer *m_actionsContainer{};
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
#include "constantsmodel.h"
|
||||
|
||||
#include "projectcontainer.h"
|
||||
|
||||
namespace {
|
||||
enum {
|
||||
ColumnName,
|
||||
ColumnValue,
|
||||
NumberOfColumns
|
||||
};
|
||||
}
|
||||
|
||||
ConstantsModel::ConstantsModel(ProjectContainer &project, QObject *parent) :
|
||||
QAbstractTableModel{parent},
|
||||
m_project{project}
|
||||
{
|
||||
}
|
||||
|
||||
int ConstantsModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConstantsModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return NumberOfColumns;
|
||||
}
|
||||
|
||||
QVariant ConstantsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
Q_UNUSED(index)
|
||||
Q_UNUSED(role)
|
||||
return {};
|
||||
}
|
||||
|
||||
QVariant ConstantsModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation != Qt::Horizontal || (role != Qt::DisplayRole && role != Qt::EditRole))
|
||||
return {};
|
||||
|
||||
switch (section)
|
||||
{
|
||||
case ColumnName: return tr("Name");
|
||||
case ColumnValue: return tr("Value");
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
|
||||
struct ProjectContainer;
|
||||
|
||||
class ConstantsModel : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConstantsModel(ProjectContainer &project, QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
int columnCount(const QModelIndex &parent) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
|
||||
private:
|
||||
ProjectContainer &m_project;
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "includedfilesmodel.h"
|
||||
|
||||
#include "projectcontainer.h"
|
||||
|
||||
IncludedFilesModel::IncludedFilesModel(ProjectContainer &project, QObject *parent) :
|
||||
QAbstractListModel{parent},
|
||||
m_project{project}
|
||||
{
|
||||
}
|
||||
|
||||
int IncludedFilesModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return 0;
|
||||
}
|
||||
|
||||
QVariant IncludedFilesModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
Q_UNUSED(index)
|
||||
Q_UNUSED(role)
|
||||
return {};
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
struct ProjectContainer;
|
||||
|
||||
class IncludedFilesModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit IncludedFilesModel(ProjectContainer &project, QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
|
||||
private:
|
||||
ProjectContainer &m_project;
|
||||
};
|
||||
@@ -0,0 +1,178 @@
|
||||
#include "objecteventsmodel.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QIcon>
|
||||
|
||||
#include "futurecpp.h"
|
||||
|
||||
ObjectEventsModel::ObjectEventsModel(Object::events_container_t &events, QObject *parent) :
|
||||
QAbstractListModel{parent},
|
||||
m_events{events}
|
||||
{
|
||||
}
|
||||
|
||||
int ObjectEventsModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return m_events.size();
|
||||
}
|
||||
|
||||
QVariant ObjectEventsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
Q_UNUSED(index)
|
||||
|
||||
if (index.row() < 0 || std::size_t(index.row()) >= m_events.size())
|
||||
{
|
||||
qWarning() << "row out of bounds" << index.row();
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto &pair = *std::next(std::cbegin(m_events), index.row());
|
||||
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
switch (pair.first)
|
||||
{
|
||||
case Object::EventType::Create: return tr("Create");
|
||||
case Object::EventType::Destroy: return tr("Destroy");
|
||||
case Object::EventType::Step: return tr("Step");
|
||||
case Object::EventType::Draw: return tr("Draw");
|
||||
default:
|
||||
qWarning() << "unknown event type" << std::to_underlying(pair.first);
|
||||
return QString::number(std::to_underlying(pair.first));
|
||||
}
|
||||
case Qt::DecorationRole:
|
||||
switch (pair.first)
|
||||
{
|
||||
case Object::EventType::Create: return QIcon{":/qtgameengine/icons/event-create.png"};
|
||||
case Object::EventType::Destroy: return QIcon{":/qtgameengine/icons/event-destroy.png"};
|
||||
case Object::EventType::Step: return QIcon{":/qtgameengine/icons/event-step.png"};
|
||||
case Object::EventType::Draw: return QIcon{":/qtgameengine/icons/event-draw.png"};
|
||||
default:
|
||||
qWarning() << "unknown event type" << std::to_underlying(pair.first);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool ObjectEventsModel::removeRows(int row, int count, const QModelIndex &parent)
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
|
||||
if (row < 0 || std::size_t(row) >= m_events.size())
|
||||
{
|
||||
qWarning() << "unexpected row" << row;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count < 0 || std::size_t(count) > m_events.size() - row)
|
||||
{
|
||||
qWarning() << "unexpected row+count" << count << row;
|
||||
return false;
|
||||
}
|
||||
|
||||
beginRemoveRows({}, row, row + count - 1);
|
||||
auto begin = std::next(std::begin(m_events), row);
|
||||
auto end = std::next(begin, count);
|
||||
m_events.erase(begin, end);
|
||||
endRemoveRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ObjectEventsModel::addEvent(Object::EventType eventType)
|
||||
{
|
||||
if (m_events.contains(eventType))
|
||||
{
|
||||
qWarning() << std::to_underlying(eventType) << "duplicate";
|
||||
return false;
|
||||
}
|
||||
|
||||
beginResetModel();
|
||||
m_events[eventType];
|
||||
endResetModel();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ObjectEventsModel::changeEvent(Object::EventType eventType, Object::EventType newEventType)
|
||||
{
|
||||
const auto iter = m_events.find(eventType);
|
||||
if (iter == std::cend(m_events))
|
||||
{
|
||||
qWarning() << std::to_underlying(eventType) << "not found";
|
||||
return false;
|
||||
}
|
||||
|
||||
beginResetModel();
|
||||
auto value = std::move(iter->second);
|
||||
m_events.erase(iter);
|
||||
m_events[newEventType] = std::move(value);
|
||||
endResetModel();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ObjectEventsModel::removeEvent(Object::EventType eventType)
|
||||
{
|
||||
const auto iter = m_events.find(eventType);
|
||||
if (iter == std::cend(m_events))
|
||||
{
|
||||
qWarning() << std::to_underlying(eventType) << "not found";
|
||||
return false;
|
||||
}
|
||||
|
||||
beginResetModel();
|
||||
m_events.erase(iter);
|
||||
endResetModel();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::pair<const Object::EventType, ActionsContainer> *ObjectEventsModel::getEvent(const QModelIndex &index)
|
||||
{
|
||||
if (!index.isValid())
|
||||
{
|
||||
qWarning() << "unexpected invalid index";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return getEvent(index.row());
|
||||
}
|
||||
|
||||
const std::pair<const Object::EventType, ActionsContainer> *ObjectEventsModel::getEvent(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
{
|
||||
qWarning() << "unexpected invalid index";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return getEvent(index.row());
|
||||
}
|
||||
|
||||
std::pair<const Object::EventType, ActionsContainer> *ObjectEventsModel::getEvent(int row)
|
||||
{
|
||||
if (row < 0 || std::size_t(row) >= m_events.size())
|
||||
{
|
||||
qWarning() << "unexpected row" << row;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &*std::next(std::begin(m_events), row);
|
||||
}
|
||||
|
||||
const std::pair<const Object::EventType, ActionsContainer> *ObjectEventsModel::getEvent(int row) const
|
||||
{
|
||||
if (row < 0 || std::size_t(row) >= m_events.size())
|
||||
{
|
||||
qWarning() << "unexpected row" << row;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &*std::next(std::cbegin(m_events), row);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "projectcontainer.h"
|
||||
|
||||
class ObjectEventsModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ObjectEventsModel(Object::events_container_t &events, QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex{}) override;
|
||||
|
||||
bool addEvent(Object::EventType eventType);
|
||||
bool changeEvent(Object::EventType eventType, Object::EventType newEventType);
|
||||
bool removeEvent(Object::EventType eventType);
|
||||
|
||||
std::pair<const Object::EventType, ActionsContainer> *getEvent(const QModelIndex &index);
|
||||
const std::pair<const Object::EventType, ActionsContainer> *getEvent(const QModelIndex &index) const;
|
||||
std::pair<const Object::EventType, ActionsContainer> *getEvent(int row);
|
||||
const std::pair<const Object::EventType, ActionsContainer> *getEvent(int row) const;
|
||||
|
||||
private:
|
||||
Object::events_container_t &m_events;
|
||||
};
|
||||
@@ -0,0 +1,196 @@
|
||||
#include "pathpointsmodel.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
namespace {
|
||||
enum {
|
||||
ColumnPoint,
|
||||
ColumnSp,
|
||||
NumberOfColumns
|
||||
};
|
||||
}
|
||||
|
||||
PathPointsModel::PathPointsModel(std::vector<Path::Point> &points, QObject *parent) :
|
||||
QAbstractTableModel{parent},
|
||||
m_points{points}
|
||||
{
|
||||
}
|
||||
|
||||
int PathPointsModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return m_points.size();
|
||||
}
|
||||
|
||||
int PathPointsModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return NumberOfColumns;
|
||||
}
|
||||
|
||||
QVariant PathPointsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (index.row() < 0 || std::size_t(index.row()) >= m_points.size())
|
||||
{
|
||||
qWarning() << "invalid row" << index.row();
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto &point = m_points.at(index.row());
|
||||
|
||||
switch (index.column())
|
||||
{
|
||||
case ColumnPoint:
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DisplayRole: return tr("(%0,%1)").arg(point.point.x()).arg(point.point.y());
|
||||
case Qt::EditRole: return point.point;
|
||||
default: return {};
|
||||
}
|
||||
case ColumnSp:
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole: return point.sp;
|
||||
default: return {};
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QVariant PathPointsModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation != Qt::Horizontal || (role != Qt::DisplayRole && role != Qt::EditRole))
|
||||
return {};
|
||||
|
||||
switch (section)
|
||||
{
|
||||
case ColumnPoint: return tr("point");
|
||||
case ColumnSp: return tr("sp");
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool PathPointsModel::insertRows(int row, int count, const QModelIndex &parent)
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
|
||||
if (row < 0 || std::size_t(row) > m_points.size())
|
||||
{
|
||||
qWarning() << "unexpected row" << row;
|
||||
return false;
|
||||
}
|
||||
|
||||
beginInsertRows({}, row, row + count - 1);
|
||||
for (size_t i = 0; i < std::size_t(count); i++)
|
||||
m_points.emplace(std::next(std::begin(m_points), row + i));
|
||||
endInsertRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PathPointsModel::removeRows(int row, int count, const QModelIndex &parent)
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
|
||||
if (row < 0 || std::size_t(row) >= m_points.size())
|
||||
{
|
||||
qWarning() << "unexpected row" << row;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count < 0 || std::size_t(count) > m_points.size() - row)
|
||||
{
|
||||
qWarning() << "unexpected row+count" << count << row;
|
||||
return false;
|
||||
}
|
||||
|
||||
beginRemoveRows({}, row, row + count - 1);
|
||||
auto begin = std::next(std::begin(m_points), row);
|
||||
auto end = std::next(begin, count);
|
||||
m_points.erase(begin, end);
|
||||
endRemoveRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<Path::Point> PathPointsModel::getPoint(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
{
|
||||
qWarning() << "unexpected invalid index" << index;
|
||||
return {};
|
||||
}
|
||||
|
||||
return getPoint(index.row());
|
||||
}
|
||||
|
||||
std::optional<Path::Point> PathPointsModel::getPoint(int row) const
|
||||
{
|
||||
if (row < 0 || std::size_t(row) >= m_points.size())
|
||||
{
|
||||
qWarning() << "unexpected row" << row;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return m_points.at(row);
|
||||
}
|
||||
|
||||
bool PathPointsModel::insertPoint(std::size_t index, Path::Point point)
|
||||
{
|
||||
if (index > m_points.size())
|
||||
{
|
||||
qWarning() << "unexpected index" << index;
|
||||
return false;
|
||||
}
|
||||
|
||||
beginInsertRows({}, index, index);
|
||||
m_points.emplace(std::next(std::begin(m_points), index), point);
|
||||
endInsertRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PathPointsModel::updatePoint(std::size_t index, Path::Point point)
|
||||
{
|
||||
if (index >= m_points.size())
|
||||
{
|
||||
qWarning() << "unexpected index" << index;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_points.at(index) = point;
|
||||
const auto modelIndex = this->index(index, 0);
|
||||
emit dataChanged(modelIndex, modelIndex, { Qt::DisplayRole, Qt::EditRole });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PathPointsModel::removePoint(std::size_t index)
|
||||
{
|
||||
if (index >= m_points.size())
|
||||
{
|
||||
qWarning() << "unexpected index" << index;
|
||||
return false;
|
||||
}
|
||||
|
||||
beginRemoveRows({}, index, index);
|
||||
m_points.erase(std::next(std::begin(m_points), index));
|
||||
endRemoveRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PathPointsModel::pointInserted(std::size_t index)
|
||||
{
|
||||
beginInsertRows({}, index, index);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void PathPointsModel::pointMoved(std::size_t index)
|
||||
{
|
||||
const auto modelIndex = this->index(index, 0);
|
||||
emit dataChanged(modelIndex, modelIndex, {Qt::DisplayRole, Qt::EditRole});
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "projectcontainer.h"
|
||||
|
||||
class PathPointsModel : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PathPointsModel(std::vector<Path::Point> &points, 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) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex{}) override;
|
||||
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex{}) override;
|
||||
|
||||
std::optional<Path::Point> getPoint(const QModelIndex &index) const;
|
||||
std::optional<Path::Point> getPoint(int row) const;
|
||||
|
||||
bool insertPoint(std::size_t index, Path::Point point);
|
||||
bool updatePoint(std::size_t index, Path::Point point);
|
||||
bool removePoint(std::size_t index);
|
||||
|
||||
public slots:
|
||||
void pointInserted(std::size_t index);
|
||||
void pointMoved(std::size_t index);
|
||||
|
||||
private:
|
||||
std::vector<Path::Point> &m_points;
|
||||
};
|
||||
@@ -0,0 +1,999 @@
|
||||
#include "projecttreemodel.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QPixmap>
|
||||
#include <QIcon>
|
||||
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
|
||||
#include "futurecpp.h"
|
||||
#include "projectcontainer.h"
|
||||
|
||||
namespace {
|
||||
enum {
|
||||
RowSprites,
|
||||
RowSounds,
|
||||
RowBackgrounds,
|
||||
RowPaths,
|
||||
RowScripts,
|
||||
RowFonts,
|
||||
RowTimeLines,
|
||||
RowObjects,
|
||||
RowRooms,
|
||||
RowGameInformation,
|
||||
RowGlobalGameSettings,
|
||||
RowExtensionPackages,
|
||||
NumberOfRows
|
||||
};
|
||||
}
|
||||
|
||||
ProjectTreeModel::ProjectTreeModel(QObject *parent) :
|
||||
QAbstractItemModel{parent}
|
||||
{
|
||||
}
|
||||
|
||||
ProjectTreeModel::ProjectTreeModel(ProjectContainer &project, QObject *parent) :
|
||||
QAbstractItemModel{parent},
|
||||
m_project{&project}
|
||||
{
|
||||
}
|
||||
|
||||
QModelIndex ProjectTreeModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
if (!parent.isValid())
|
||||
return createIndex(row, column, std::to_underlying(NodeType::Root));
|
||||
else if (!parent.parent().isValid())
|
||||
{
|
||||
switch (nodeType(parent))
|
||||
{
|
||||
case NodeType::Root:
|
||||
switch (parent.row())
|
||||
{
|
||||
case RowSprites: return createIndex(row, column, std::to_underlying(NodeType::Sprite));
|
||||
case RowSounds: return createIndex(row, column, std::to_underlying(NodeType::Sound));
|
||||
case RowBackgrounds: return createIndex(row, column, std::to_underlying(NodeType::Background));
|
||||
case RowPaths: return createIndex(row, column, std::to_underlying(NodeType::Path));
|
||||
case RowScripts: return createIndex(row, column, std::to_underlying(NodeType::Script));
|
||||
case RowFonts: return createIndex(row, column, std::to_underlying(NodeType::Font));
|
||||
case RowTimeLines: return createIndex(row, column, std::to_underlying(NodeType::TimeLine));
|
||||
case RowObjects: return createIndex(row, column, std::to_underlying(NodeType::Object));
|
||||
case RowRooms: return createIndex(row, column, std::to_underlying(NodeType::Room));
|
||||
default:
|
||||
qWarning() << "unexpected parent row" << parent.row();
|
||||
return {};
|
||||
}
|
||||
default:
|
||||
qWarning() << "unexpected parent type" << parent.internalId();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning() << "unexpected parent";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
QModelIndex ProjectTreeModel::parent(const QModelIndex &child) const
|
||||
{
|
||||
switch (nodeType(child))
|
||||
{
|
||||
case NodeType::Root: return {};
|
||||
case NodeType::Sprite: return rootFor<Sprite>();
|
||||
case NodeType::Sound: return rootFor<Sound>();
|
||||
case NodeType::Background: return rootFor<Background>();
|
||||
case NodeType::Path: return rootFor<Path>();
|
||||
case NodeType::Script: return rootFor<Script>();
|
||||
case NodeType::Font: return rootFor<Font>();
|
||||
case NodeType::TimeLine: return rootFor<TimeLine>();
|
||||
case NodeType::Object: return rootFor<Object>();
|
||||
case NodeType::Room: return rootFor<Room>();
|
||||
default:
|
||||
qWarning() << "unexpected child type" << child.internalId();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
int ProjectTreeModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (!parent.isValid())
|
||||
return m_project ? NumberOfRows : 0;
|
||||
|
||||
switch (nodeType(parent))
|
||||
{
|
||||
case NodeType::Root:
|
||||
switch (parent.row())
|
||||
{
|
||||
case RowSprites: return m_project ? m_project->containerFor<Sprite>().size() : 0;
|
||||
case RowSounds: return m_project ? m_project->containerFor<Sound>().size() : 0;
|
||||
case RowBackgrounds: return m_project ? m_project->containerFor<Background>().size() : 0;
|
||||
case RowPaths: return m_project ? m_project->containerFor<Path>().size() : 0;
|
||||
case RowScripts: return m_project ? m_project->containerFor<Script>().size() : 0;
|
||||
case RowFonts: return m_project ? m_project->containerFor<Font>().size() : 0;
|
||||
case RowTimeLines: return m_project ? m_project->containerFor<TimeLine>().size() : 0;
|
||||
case RowObjects: return m_project ? m_project->containerFor<Object>().size() : 0;
|
||||
case RowRooms: return m_project ? m_project->containerFor<Room>().size() : 0;
|
||||
case RowGameInformation:
|
||||
case RowGlobalGameSettings:
|
||||
case RowExtensionPackages:
|
||||
return 0;
|
||||
default:
|
||||
qWarning() << "unexpected parent row" << parent.row();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return {};
|
||||
case NodeType::Sprite:
|
||||
case NodeType::Sound:
|
||||
case NodeType::Background:
|
||||
case NodeType::Path:
|
||||
case NodeType::Script:
|
||||
case NodeType::Font:
|
||||
case NodeType::TimeLine:
|
||||
case NodeType::Object:
|
||||
case NodeType::Room:
|
||||
return 0;
|
||||
default:
|
||||
qWarning() << "unexpected parent child type" << parent.internalId();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int ProjectTreeModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (!parent.isValid())
|
||||
return 1;
|
||||
|
||||
switch (nodeType(parent))
|
||||
{
|
||||
case NodeType::Root:
|
||||
switch (parent.row())
|
||||
{
|
||||
case RowSprites:
|
||||
case RowSounds:
|
||||
case RowBackgrounds:
|
||||
case RowPaths:
|
||||
case RowScripts:
|
||||
case RowFonts:
|
||||
case RowTimeLines:
|
||||
case RowObjects:
|
||||
case RowRooms:
|
||||
case RowGameInformation:
|
||||
case RowGlobalGameSettings:
|
||||
case RowExtensionPackages:
|
||||
return 1;
|
||||
default:
|
||||
qWarning() << "unexpected parent row" << parent.row();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return {};
|
||||
case NodeType::Sprite:
|
||||
case NodeType::Sound:
|
||||
case NodeType::Background:
|
||||
case NodeType::Path:
|
||||
case NodeType::Script:
|
||||
case NodeType::Font:
|
||||
case NodeType::TimeLine:
|
||||
case NodeType::Object:
|
||||
case NodeType::Room:
|
||||
return 1;
|
||||
default:
|
||||
qWarning() << "unexpected parent child type" << parent.internalId();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
QVariant ProjectTreeModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
{
|
||||
qWarning() << "invalid index";
|
||||
return {};
|
||||
}
|
||||
|
||||
switch (nodeType(index))
|
||||
{
|
||||
case NodeType::Root:
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
switch (index.row())
|
||||
{
|
||||
case RowSprites: return tr("Sprites");
|
||||
case RowSounds: return tr("Sounds");
|
||||
case RowBackgrounds: return tr("Backgrounds");
|
||||
case RowPaths: return tr("Paths");
|
||||
case RowScripts: return tr("Scripts");
|
||||
case RowFonts: return tr("Fonts");
|
||||
case RowTimeLines: return tr("Time Lines");
|
||||
case RowObjects: return tr("Objects");
|
||||
case RowRooms: return tr("Rooms");
|
||||
case RowGameInformation: return tr("Game Information");
|
||||
case RowGlobalGameSettings: return tr("Global Game Settings");
|
||||
case RowExtensionPackages: return tr("Extension Packages");
|
||||
default:
|
||||
qWarning() << "unexpected root row" << index.row();
|
||||
return {};
|
||||
}
|
||||
case Qt::DecorationRole:
|
||||
switch (index.row())
|
||||
{
|
||||
case RowSprites:
|
||||
case RowSounds:
|
||||
case RowBackgrounds:
|
||||
case RowPaths:
|
||||
case RowScripts:
|
||||
case RowFonts:
|
||||
case RowTimeLines:
|
||||
case RowObjects:
|
||||
case RowRooms: return QIcon{":/qtgameengine/icons/folder.png"};
|
||||
case RowGameInformation: return QIcon{":/qtgameengine/icons/game-information-file.png"};
|
||||
case RowGlobalGameSettings: return QIcon{":/qtgameengine/icons/global-game-settings-file.png"};
|
||||
case RowExtensionPackages: return QIcon{":/qtgameengine/icons/extension-packages-file.png"};
|
||||
default:
|
||||
qWarning() << "unexpected root row" << index.row();
|
||||
return {};
|
||||
}
|
||||
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
case NodeType::Sprite: return dataFor<Sprite>(index, role);
|
||||
case NodeType::Sound: return dataFor<Sound>(index, role);
|
||||
case NodeType::Background: return dataFor<Background>(index, role);
|
||||
case NodeType::Path: return dataFor<Path>(index, role);
|
||||
case NodeType::Script: return dataFor<Script>(index, role);
|
||||
case NodeType::Font: return dataFor<Font>(index, role);
|
||||
case NodeType::TimeLine: return dataFor<TimeLine>(index, role);
|
||||
case NodeType::Object: return dataFor<Object>(index, role);
|
||||
case NodeType::Room: return dataFor<Room>(index, role);
|
||||
default:
|
||||
qWarning() << "unexpected index type" << index.internalId();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
Qt::ItemFlags ProjectTreeModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
auto flags = QAbstractItemModel::flags(index);
|
||||
|
||||
switch (nodeType(index))
|
||||
{
|
||||
case NodeType::Root:
|
||||
break;
|
||||
case NodeType::Sprite:
|
||||
case NodeType::Sound:
|
||||
case NodeType::Background:
|
||||
case NodeType::Path:
|
||||
case NodeType::Script:
|
||||
case NodeType::Font:
|
||||
case NodeType::TimeLine:
|
||||
case NodeType::Object:
|
||||
case NodeType::Room:
|
||||
flags |= Qt::ItemIsEditable;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
bool ProjectTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
switch (const auto nodeType = this->nodeType(index))
|
||||
{
|
||||
case NodeType::Sprite: return setDataFor<Sprite>(index, value, role);
|
||||
case NodeType::Sound: return setDataFor<Sound>(index, value, role);
|
||||
case NodeType::Background: return setDataFor<Background>(index, value, role);
|
||||
case NodeType::Path: return setDataFor<Path>(index, value, role);
|
||||
case NodeType::Script: return setDataFor<Script>(index, value, role);
|
||||
case NodeType::Font: return setDataFor<Font>(index, value, role);
|
||||
case NodeType::TimeLine: return setDataFor<TimeLine>(index, value, role);
|
||||
case NodeType::Object: return setDataFor<Object>(index, value, role);
|
||||
case NodeType::Room: return setDataFor<Room>(index, value, role);
|
||||
default:
|
||||
qWarning() << "unexpected nodeType" << std::to_underlying(nodeType);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ProjectTreeModel::insertRows(int row, int count, const QModelIndex &parent)
|
||||
{
|
||||
if (auto result = insertRowsFor<Sprite>(row, count, parent)) return *result;
|
||||
if (auto result = insertRowsFor<Sound>(row, count, parent)) return *result;
|
||||
if (auto result = insertRowsFor<Background>(row, count, parent)) return *result;
|
||||
if (auto result = insertRowsFor<Path>(row, count, parent)) return *result;
|
||||
if (auto result = insertRowsFor<Script>(row, count, parent)) return *result;
|
||||
if (auto result = insertRowsFor<Font>(row, count, parent)) return *result;
|
||||
if (auto result = insertRowsFor<TimeLine>(row, count, parent)) return *result;
|
||||
if (auto result = insertRowsFor<Object>(row, count, parent)) return *result;
|
||||
if (auto result = insertRowsFor<Room>(row, count, parent)) return *result;
|
||||
|
||||
qWarning() << "unexpected parent" << parent;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProjectTreeModel::removeRows(int row, int count, const QModelIndex &parent)
|
||||
{
|
||||
if (auto result = removeRowsFor<Sprite>(row, count, parent)) return *result;
|
||||
if (auto result = removeRowsFor<Sound>(row, count, parent)) return *result;
|
||||
if (auto result = removeRowsFor<Background>(row, count, parent)) return *result;
|
||||
if (auto result = removeRowsFor<Path>(row, count, parent)) return *result;
|
||||
if (auto result = removeRowsFor<Script>(row, count, parent)) return *result;
|
||||
if (auto result = removeRowsFor<Font>(row, count, parent)) return *result;
|
||||
if (auto result = removeRowsFor<TimeLine>(row, count, parent)) return *result;
|
||||
if (auto result = removeRowsFor<Object>(row, count, parent)) return *result;
|
||||
if (auto result = removeRowsFor<Room>(row, count, parent)) return *result;
|
||||
|
||||
qWarning() << "unexpected parent" << parent;
|
||||
return false;
|
||||
}
|
||||
|
||||
void ProjectTreeModel::setProject(ProjectContainer *project)
|
||||
{
|
||||
beginResetModel();
|
||||
m_project = project;
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
ProjectTreeModel::NodeType ProjectTreeModel::nodeType(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
qWarning() << "invalid index";
|
||||
|
||||
return NodeType(index.internalId());
|
||||
}
|
||||
|
||||
template<> QModelIndex ProjectTreeModel::rootFor<Sprite>() const
|
||||
{
|
||||
return createIndex(RowSprites, 0, std::to_underlying(NodeType::Root));
|
||||
}
|
||||
|
||||
template<> QModelIndex ProjectTreeModel::rootFor<Sound>() const
|
||||
{
|
||||
return createIndex(RowSounds, 0, std::to_underlying(NodeType::Root));
|
||||
}
|
||||
|
||||
template<> QModelIndex ProjectTreeModel::rootFor<Background>() const
|
||||
{
|
||||
return createIndex(RowBackgrounds, 0, std::to_underlying(NodeType::Root));
|
||||
}
|
||||
|
||||
template<> QModelIndex ProjectTreeModel::rootFor<Path>() const
|
||||
{
|
||||
return createIndex(RowPaths, 0, std::to_underlying(NodeType::Root));
|
||||
}
|
||||
|
||||
template<> QModelIndex ProjectTreeModel::rootFor<Script>() const
|
||||
{
|
||||
return createIndex(RowScripts, 0, std::to_underlying(NodeType::Root));
|
||||
}
|
||||
|
||||
template<> QModelIndex ProjectTreeModel::rootFor<Font>() const
|
||||
{
|
||||
return createIndex(RowFonts, 0, std::to_underlying(NodeType::Root));
|
||||
}
|
||||
|
||||
template<> QModelIndex ProjectTreeModel::rootFor<TimeLine>() const
|
||||
{
|
||||
return createIndex(RowTimeLines, 0, std::to_underlying(NodeType::Root));
|
||||
}
|
||||
|
||||
template<> QModelIndex ProjectTreeModel::rootFor<Object>() const
|
||||
{
|
||||
return createIndex(RowObjects, 0, std::to_underlying(NodeType::Root));
|
||||
}
|
||||
|
||||
template<> QModelIndex ProjectTreeModel::rootFor<Room>() const
|
||||
{
|
||||
return createIndex(RowRooms, 0, std::to_underlying(NodeType::Root));
|
||||
}
|
||||
|
||||
QModelIndex ProjectTreeModel::gameInformationRoot() const
|
||||
{
|
||||
return createIndex(RowGameInformation, 0, std::to_underlying(NodeType::Root));
|
||||
}
|
||||
|
||||
QModelIndex ProjectTreeModel::globalGameSettingsRoot() const
|
||||
{
|
||||
return createIndex(RowGlobalGameSettings, 0, std::to_underlying(NodeType::Root));
|
||||
}
|
||||
|
||||
QModelIndex ProjectTreeModel::extensionPackagesRoot() const
|
||||
{
|
||||
return createIndex(RowExtensionPackages, 0, std::to_underlying(NodeType::Root));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *ProjectTreeModel::get(const QModelIndex &index)
|
||||
{
|
||||
if (!m_project)
|
||||
{
|
||||
qWarning() << "unexpected null project";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (const auto nodeType = this->nodeType(index); nodeType != nodeTypeFor<T>())
|
||||
{
|
||||
qWarning() << "unexpected index type" << std::to_underlying(nodeType);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto &container = m_project->containerFor<T>();
|
||||
if (index.row() < 0 || std::size_t(index.row()) >= container.size())
|
||||
{
|
||||
qWarning() << "invalid row" << index.row();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &*std::next(std::begin(container), index.row());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T *ProjectTreeModel::get(const QModelIndex &index) const
|
||||
{
|
||||
if (!m_project)
|
||||
{
|
||||
qWarning() << "unexpected null project";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (const auto nodeType = this->nodeType(index); nodeType != nodeTypeFor<T>())
|
||||
{
|
||||
qWarning() << "unexpected index type" << std::to_underlying(nodeType);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto &container = m_project->containerFor<T>();
|
||||
if (index.row() < 0 || std::size_t(index.row()) >= container.size())
|
||||
{
|
||||
qWarning() << "invalid row" << index.row();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &*std::next(std::cbegin(container), index.row());
|
||||
}
|
||||
|
||||
template Sprite *ProjectTreeModel::get<Sprite>(const QModelIndex &index);
|
||||
template const Sprite *ProjectTreeModel::get<Sprite>(const QModelIndex &index) const;
|
||||
template Sound *ProjectTreeModel::get<Sound>(const QModelIndex &index);
|
||||
template const Sound *ProjectTreeModel::get<Sound>(const QModelIndex &index) const;
|
||||
template Background *ProjectTreeModel::get<Background>(const QModelIndex &index);
|
||||
template const Background *ProjectTreeModel::get<Background>(const QModelIndex &index) const;
|
||||
template Path *ProjectTreeModel::get<Path>(const QModelIndex &index);
|
||||
template const Path *ProjectTreeModel::get<Path>(const QModelIndex &index) const;
|
||||
template Script *ProjectTreeModel::get<Script>(const QModelIndex &index);
|
||||
template const Script *ProjectTreeModel::get<Script>(const QModelIndex &index) const;
|
||||
template Font *ProjectTreeModel::get<Font>(const QModelIndex &index);
|
||||
template const Font *ProjectTreeModel::get<Font>(const QModelIndex &index) const;
|
||||
template TimeLine *ProjectTreeModel::get<TimeLine>(const QModelIndex &index);
|
||||
template const TimeLine *ProjectTreeModel::get<TimeLine>(const QModelIndex &index) const;
|
||||
template Object *ProjectTreeModel::get<Object>(const QModelIndex &index);
|
||||
template const Object *ProjectTreeModel::get<Object>(const QModelIndex &index) const;
|
||||
template Room *ProjectTreeModel::get<Room>(const QModelIndex &index);
|
||||
template const Room *ProjectTreeModel::get<Room>(const QModelIndex &index) const;
|
||||
|
||||
template<typename T>
|
||||
T *ProjectTreeModel::create()
|
||||
{
|
||||
if (!m_project)
|
||||
{
|
||||
qWarning() << "unexpected null project";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto &container = m_project->containerFor<T>();
|
||||
|
||||
beginInsertRows(rootFor<T>(), container.size(), container.size());
|
||||
container.emplace_back(T { .name = getFreeNameFor<T>(container) });
|
||||
endInsertRows();
|
||||
|
||||
return &container.back();
|
||||
}
|
||||
|
||||
template Sprite *ProjectTreeModel::create<Sprite>();
|
||||
template Sound *ProjectTreeModel::create<Sound>();
|
||||
template Background *ProjectTreeModel::create<Background>();
|
||||
template Path *ProjectTreeModel::create<Path>();
|
||||
template Script *ProjectTreeModel::create<Script>();
|
||||
template Font *ProjectTreeModel::create<Font>();
|
||||
template TimeLine *ProjectTreeModel::create<TimeLine>();
|
||||
template Object *ProjectTreeModel::create<Object>();
|
||||
template Room *ProjectTreeModel::create<Room>();
|
||||
|
||||
template<typename T>
|
||||
bool ProjectTreeModel::remove(const T &entry)
|
||||
{
|
||||
if (!m_project)
|
||||
{
|
||||
qWarning() << "unexpected null project";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto &container = m_project->containerFor<T>();
|
||||
const auto iter = std::find_if(std::begin(container), std::end(container),
|
||||
[&entry](const auto &otherEntry){ return &entry == &otherEntry; });
|
||||
if (iter == std::cend(container))
|
||||
{
|
||||
qWarning() << "entry not from this project!";
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto row = std::distance(std::begin(container), iter);
|
||||
|
||||
onBeforeRemove<T>(*iter);
|
||||
|
||||
emitAboutToBeRemoved<T>(*iter);
|
||||
|
||||
beginRemoveRows(rootFor<T>(), row, row);
|
||||
container.erase(iter);
|
||||
endRemoveRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template bool ProjectTreeModel::remove<Sprite>(const Sprite &entry);
|
||||
template bool ProjectTreeModel::remove<Sound>(const Sound &entry);
|
||||
template bool ProjectTreeModel::remove<Background>(const Background &entry);
|
||||
template bool ProjectTreeModel::remove<Path>(const Path &entry);
|
||||
template bool ProjectTreeModel::remove<Script>(const Script &entry);
|
||||
template bool ProjectTreeModel::remove<Font>(const Font &entry);
|
||||
template bool ProjectTreeModel::remove<TimeLine>(const TimeLine &entry);
|
||||
template bool ProjectTreeModel::remove<Object>(const Object &entry);
|
||||
template bool ProjectTreeModel::remove<Room>(const Room &entry);
|
||||
|
||||
template<typename T>
|
||||
bool ProjectTreeModel::rename(const T &entry, const QString &newName)
|
||||
{
|
||||
if (!m_project)
|
||||
{
|
||||
qWarning() << "unexpected null project";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto &container = m_project->containerFor<T>();
|
||||
const auto iter = std::find_if(std::begin(container), std::end(container),
|
||||
[&entry](const auto &otherEntry){ return &entry == &otherEntry; });
|
||||
if (iter == std::cend(container))
|
||||
{
|
||||
qWarning() << "entry not from this project!";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (iter->name == newName)
|
||||
return true;
|
||||
|
||||
if (std::any_of(std::cbegin(container), std::cend(container),
|
||||
[&newName](const auto &entry){ return entry.name == newName; }))
|
||||
{
|
||||
qWarning() << "duplicate name" << newName;
|
||||
return false;
|
||||
}
|
||||
|
||||
onBeforeRename<T>(*iter, newName);
|
||||
|
||||
auto oldName = std::move(iter->name);
|
||||
iter->name = std::move(newName);
|
||||
|
||||
emitNameChanged<T>(*iter, oldName);
|
||||
const auto index = this->index(std::distance(std::begin(container), iter), 0, rootFor<T>());
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
|
||||
onAfterRename<T>(*iter, std::move(oldName));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template bool ProjectTreeModel::rename<Sprite>(const Sprite &entry, const QString &newName);
|
||||
template bool ProjectTreeModel::rename<Sound>(const Sound &entry, const QString &newName);
|
||||
template bool ProjectTreeModel::rename<Background>(const Background &entry, const QString &newName);
|
||||
template bool ProjectTreeModel::rename<Path>(const Path &entry, const QString &newName);
|
||||
template bool ProjectTreeModel::rename<Script>(const Script &entry, const QString &newName);
|
||||
template bool ProjectTreeModel::rename<Font>(const Font &entry, const QString &newName);
|
||||
template bool ProjectTreeModel::rename<TimeLine>(const TimeLine &entry, const QString &newName);
|
||||
template bool ProjectTreeModel::rename<Object>(const Object &entry, const QString &newName);
|
||||
template bool ProjectTreeModel::rename<Room>(const Room &entry, const QString &newName);
|
||||
|
||||
bool ProjectTreeModel::setSpritePixmaps(const Sprite &sprite, std::vector<QPixmap> &&pixmaps)
|
||||
{
|
||||
if (!m_project)
|
||||
{
|
||||
qWarning() << "unexpected null project";
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
auto &container = m_project->sprites;
|
||||
const auto iter = std::find_if(std::begin(container), std::end(container),
|
||||
[&sprite](const auto &otherEntry){ return &sprite == &otherEntry; });
|
||||
if (iter == std::cend(container))
|
||||
{
|
||||
qWarning() << "sprite not from this project!";
|
||||
return false;
|
||||
}
|
||||
|
||||
iter->pixmaps = std::move(pixmaps);
|
||||
|
||||
const auto index = this->index(std::distance(std::begin(container), iter), 0, rootFor<Sprite>());
|
||||
emit dataChanged(index, index, { Qt::DecorationRole });
|
||||
|
||||
emit spritePixmapsChanged(*iter);
|
||||
}
|
||||
|
||||
for (auto iter = std::cbegin(m_project->objects); iter != std::cend(m_project->objects); iter++)
|
||||
{
|
||||
if (iter->spriteName != sprite.name)
|
||||
continue;
|
||||
|
||||
const auto index = this->index(std::distance(std::cbegin(m_project->objects), iter), 0, rootFor<Object>());
|
||||
emit dataChanged(index, index, {Qt::DecorationRole});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProjectTreeModel::setBackgroundPixmap(const Background &background, QPixmap &&pixmap)
|
||||
{
|
||||
if (!m_project)
|
||||
{
|
||||
qWarning() << "unexpected null project";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto &container = m_project->backgrounds;
|
||||
const auto iter = std::find_if(std::begin(container), std::end(container),
|
||||
[&background](const auto &otherEntry){ return &background == &otherEntry; });
|
||||
if (iter == std::cend(container))
|
||||
{
|
||||
qWarning() << "background not from this project!";
|
||||
return false;
|
||||
}
|
||||
|
||||
iter->pixmap = std::move(pixmap);
|
||||
|
||||
const auto index = this->index(std::distance(std::begin(container), iter), 0, rootFor<Background>());
|
||||
emit dataChanged(index, index, { Qt::DecorationRole });
|
||||
|
||||
emit backgroundPixmapChanged(*iter);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
QVariant ProjectTreeModel::dataFor(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!m_project)
|
||||
{
|
||||
qWarning() << "unexpected null project";
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto &container = m_project->containerFor<T>();
|
||||
if (std::size_t(index.row()) >= container.size())
|
||||
{
|
||||
qWarning() << "index out of bounds" << index.row();
|
||||
return {};
|
||||
}
|
||||
|
||||
auto iter = std::next(std::cbegin(container), index.row());
|
||||
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
return iter->name;
|
||||
case Qt::DecorationRole:
|
||||
return iconFor<T>(*iter);
|
||||
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
QVariant ProjectTreeModel::iconFor<Sprite>(const Sprite &entry) const
|
||||
{
|
||||
if (entry.pixmaps.empty() || entry.pixmaps.front().isNull())
|
||||
{
|
||||
QPixmap pixmap{16, 16};
|
||||
pixmap.fill(Qt::white);
|
||||
return pixmap;
|
||||
}
|
||||
return QIcon{entry.pixmaps.front()};
|
||||
}
|
||||
|
||||
template<>
|
||||
QVariant ProjectTreeModel::iconFor<Sound>(const Sound &entry) const
|
||||
{
|
||||
switch (entry.type)
|
||||
{
|
||||
case Sound::Type::Sound:
|
||||
return QIcon{":/qtgameengine/icons/sound-file.png"};
|
||||
case Sound::Type::Music:
|
||||
return QIcon{":/qtgameengine/icons/music-file.png"};
|
||||
default:
|
||||
qWarning() << "unexpected sound type" << std::to_underlying(entry.type);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
QVariant ProjectTreeModel::iconFor<Background>(const Background &entry) const
|
||||
{
|
||||
if (entry.pixmap.isNull())
|
||||
{
|
||||
QPixmap pixmap{64, 64};
|
||||
pixmap.fill(Qt::white);
|
||||
return QIcon{pixmap};
|
||||
}
|
||||
return QIcon{entry.pixmap};
|
||||
}
|
||||
|
||||
template<>
|
||||
QVariant ProjectTreeModel::iconFor<Path>(const Path &entry) const
|
||||
{
|
||||
Q_UNUSED(entry)
|
||||
return QIcon{":/qtgameengine/icons/path-file.png"};
|
||||
}
|
||||
|
||||
template<>
|
||||
QVariant ProjectTreeModel::iconFor<Script>(const Script &entry) const
|
||||
{
|
||||
Q_UNUSED(entry)
|
||||
return QIcon{":/qtgameengine/icons/script-file.png"};
|
||||
}
|
||||
|
||||
template<>
|
||||
QVariant ProjectTreeModel::iconFor<Font>(const Font &entry) const
|
||||
{
|
||||
Q_UNUSED(entry)
|
||||
return QIcon{":/qtgameengine/icons/font-file.png"};
|
||||
}
|
||||
|
||||
template<>
|
||||
QVariant ProjectTreeModel::iconFor<TimeLine>(const TimeLine &entry) const
|
||||
{
|
||||
Q_UNUSED(entry)
|
||||
return QIcon{":/qtgameengine/icons/timeline-file.png"};
|
||||
}
|
||||
|
||||
template<>
|
||||
QVariant ProjectTreeModel::iconFor<Object>(const Object &entry) const
|
||||
{
|
||||
if (m_project && !entry.spriteName.isEmpty())
|
||||
{
|
||||
const auto iter = std::find_if(std::cbegin(m_project->sprites), std::cend(m_project->sprites),
|
||||
[&](const Sprite &sprite){ return sprite.name == entry.spriteName; });
|
||||
if (iter == std::cend(m_project->sprites))
|
||||
qWarning() << "sprite" << entry.spriteName << "not found";
|
||||
else if (!iter->pixmaps.empty() && !iter->pixmaps.front().isNull())
|
||||
return QIcon{iter->pixmaps.front()};
|
||||
}
|
||||
|
||||
QPixmap pixmap{64, 64};
|
||||
pixmap.fill(Qt::white);
|
||||
return QIcon{pixmap};
|
||||
}
|
||||
|
||||
template<>
|
||||
QVariant ProjectTreeModel::iconFor<Room>(const Room &entry) const
|
||||
{
|
||||
Q_UNUSED(entry)
|
||||
return QIcon{":/qtgameengine/icons/room-file.png"};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool ProjectTreeModel::setDataFor(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if (!m_project)
|
||||
{
|
||||
qWarning() << "unexpected null project";
|
||||
return {};
|
||||
}
|
||||
|
||||
auto &container = m_project->containerFor<T>();
|
||||
if (index.row() < 0 || std::size_t(index.row()) >= container.size())
|
||||
{
|
||||
qWarning() << "unexpected row" << index.row();
|
||||
return false;
|
||||
}
|
||||
auto iter = std::next(std::begin(container), index.row());
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
if (auto name = value.toString(); name != iter->name)
|
||||
{
|
||||
if (std::any_of(std::cbegin(container), std::cend(container),
|
||||
[&name](const auto &entry){ return entry.name == name; }))
|
||||
{
|
||||
qWarning() << "duplicate name" << name;
|
||||
emit errorOccured(tr("A Sprite with the name \"%0\" is already existing").arg(name));
|
||||
return false;
|
||||
}
|
||||
onBeforeRename<T>(*iter, name);
|
||||
auto oldName = std::move(iter->name);
|
||||
iter->name = std::move(name);
|
||||
emitNameChanged<T>(*iter, oldName);
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
onAfterRename<T>(*iter, std::move(oldName));
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
qWarning() << "unexpected role" << role;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::optional<bool> ProjectTreeModel::insertRowsFor(int row, int count, const QModelIndex &parent)
|
||||
{
|
||||
if (parent != rootFor<T>())
|
||||
return std::nullopt;
|
||||
|
||||
if (!m_project)
|
||||
{
|
||||
qWarning() << "unexpected null project";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto &container = m_project->containerFor<T>();
|
||||
if (row < 0 || std::size_t(row) > container.size())
|
||||
{
|
||||
qWarning() << "unexpected row" << row;
|
||||
return false;
|
||||
}
|
||||
|
||||
beginInsertRows(parent, row, row + count - 1);
|
||||
for (size_t i = 0; i < std::size_t(count); i++)
|
||||
container.emplace(std::next(std::begin(container), row + i), T { .name = getFreeNameFor<T>(container) });
|
||||
endInsertRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::optional<bool> ProjectTreeModel::removeRowsFor(int row, int count, const QModelIndex &parent)
|
||||
{
|
||||
if (parent != rootFor<T>())
|
||||
return std::nullopt;
|
||||
|
||||
if (!m_project)
|
||||
{
|
||||
qWarning() << "unexpected null project";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto &container = m_project->containerFor<T>();
|
||||
|
||||
if (row < 0 || std::size_t(row) >= container.size())
|
||||
{
|
||||
qWarning() << "unexpected row" << row;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count < 0 || std::size_t(count) > container.size() - row)
|
||||
{
|
||||
qWarning() << "unexpected row+count" << count << row;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto begin = std::next(std::begin(container), row);
|
||||
auto end = std::next(begin, count);
|
||||
|
||||
for (auto iter = begin; iter != end; iter++)
|
||||
onBeforeRemove<T>(*iter);
|
||||
|
||||
for (auto iter = begin; iter != end; iter++)
|
||||
emitAboutToBeRemoved(*iter);
|
||||
|
||||
beginRemoveRows(parent, row, row + count - 1);
|
||||
container.erase(begin, end);
|
||||
endRemoveRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T> void ProjectTreeModel::onBeforeRemove(const T &entry)
|
||||
{
|
||||
Q_UNUSED(entry)
|
||||
}
|
||||
|
||||
template<> void ProjectTreeModel::onBeforeRemove<Sprite>(const Sprite &sprite)
|
||||
{
|
||||
for (auto iter = std::begin(m_project->objects); iter != std::end(m_project->objects); iter++)
|
||||
{
|
||||
if (iter->spriteName != sprite.name)
|
||||
continue;
|
||||
|
||||
auto oldSpriteName = std::move(iter->spriteName);
|
||||
iter->spriteName.clear();
|
||||
|
||||
const auto index = this->index(std::distance(std::begin(m_project->objects), iter), 0, rootFor<Object>());
|
||||
emit dataChanged(index, index, {Qt::DecorationRole});
|
||||
|
||||
emit objectSpriteNameChanged(*iter, std::move(oldSpriteName));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> void ProjectTreeModel::onBeforeRename(const T &entry, const QString &newName)
|
||||
{
|
||||
Q_UNUSED(entry)
|
||||
Q_UNUSED(newName)
|
||||
}
|
||||
|
||||
template<typename T> void ProjectTreeModel::onAfterRename(const T &entry, const QString &oldName)
|
||||
{
|
||||
Q_UNUSED(entry)
|
||||
Q_UNUSED(oldName)
|
||||
}
|
||||
|
||||
template<> void ProjectTreeModel::onAfterRename<Sprite>(const Sprite &sprite, const QString &oldName)
|
||||
{
|
||||
for (auto iter = std::begin(m_project->objects); iter != std::end(m_project->objects); iter++)
|
||||
{
|
||||
if (iter->spriteName != oldName)
|
||||
continue;
|
||||
|
||||
auto oldSpriteName = std::move(iter->spriteName);
|
||||
|
||||
iter->spriteName = sprite.name;
|
||||
|
||||
// const auto index = this->index(std::distance(std::begin(m_project->objects), iter), 0, rootFor<Object>());
|
||||
// emit dataChanged(index, index, {Qt::DecorationRole});
|
||||
|
||||
emit objectSpriteNameChanged(*iter, std::move(oldSpriteName));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
QString ProjectTreeModel::getFreeNameFor(const std::list<T> &container)
|
||||
{
|
||||
QString name;
|
||||
for (int i = 0; ; i++)
|
||||
{
|
||||
name = nameTempateFor<T>().arg(i);
|
||||
if (std::none_of(std::cbegin(container), std::cend(container),
|
||||
[&name](const T &entry){ return entry.name == name; }))
|
||||
break;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
template<> void ProjectTreeModel::emitCreated<Sprite>(const Sprite &entry) { emit spriteCreated(entry); }
|
||||
template<> void ProjectTreeModel::emitCreated<Sound>(const Sound &entry) { emit soundCreated(entry); }
|
||||
template<> void ProjectTreeModel::emitCreated<Background>(const Background &entry) { emit backgroundCreated(entry); }
|
||||
template<> void ProjectTreeModel::emitCreated<Path>(const Path &entry) { emit pathCreated(entry); }
|
||||
template<> void ProjectTreeModel::emitCreated<Script>(const Script &entry) { emit scriptCreated(entry); }
|
||||
template<> void ProjectTreeModel::emitCreated<Font>(const Font &entry) { emit fontCreated(entry); }
|
||||
template<> void ProjectTreeModel::emitCreated<TimeLine>(const TimeLine &entry) { emit timeLineCreated(entry); }
|
||||
template<> void ProjectTreeModel::emitCreated<Object>(const Object &entry) { emit objectCreated(entry); }
|
||||
template<> void ProjectTreeModel::emitCreated<Room>(const Room &entry) { emit roomCreated(entry); }
|
||||
template<> void ProjectTreeModel::emitAboutToBeRemoved<Sprite>(const Sprite &entry) { emit spriteAboutToBeRemoved(entry); }
|
||||
template<> void ProjectTreeModel::emitAboutToBeRemoved<Sound>(const Sound &entry) { emit soundAboutToBeRemoved(entry); }
|
||||
template<> void ProjectTreeModel::emitAboutToBeRemoved<Background>(const Background &entry) { emit backgroundAboutToBeRemoved(entry); }
|
||||
template<> void ProjectTreeModel::emitAboutToBeRemoved<Path>(const Path &entry) { emit pathAboutToBeRemoved(entry); }
|
||||
template<> void ProjectTreeModel::emitAboutToBeRemoved<Script>(const Script &entry) { emit scriptAboutToBeRemoved(entry); }
|
||||
template<> void ProjectTreeModel::emitAboutToBeRemoved<Font>(const Font &entry) { emit fontAboutToBeRemoved(entry); }
|
||||
template<> void ProjectTreeModel::emitAboutToBeRemoved<TimeLine>(const TimeLine &entry) { emit timeLineAboutToBeRemoved(entry); }
|
||||
template<> void ProjectTreeModel::emitAboutToBeRemoved<Object>(const Object &entry) { emit objectAboutToBeRemoved(entry); }
|
||||
template<> void ProjectTreeModel::emitAboutToBeRemoved<Room>(const Room &entry) { emit roomAboutToBeRemoved(entry); }
|
||||
template<> void ProjectTreeModel::emitNameChanged<Sprite>(const Sprite &entry, const QString &oldName) { emit spriteNameChanged(entry, oldName); }
|
||||
template<> void ProjectTreeModel::emitNameChanged<Sound>(const Sound &entry, const QString &oldName) { emit soundNameChanged(entry, oldName); }
|
||||
template<> void ProjectTreeModel::emitNameChanged<Background>(const Background &entry, const QString &oldName) { emit backgroundNameChanged(entry, oldName); }
|
||||
template<> void ProjectTreeModel::emitNameChanged<Path>(const Path &entry, const QString &oldName) { emit pathNameChanged(entry, oldName); }
|
||||
template<> void ProjectTreeModel::emitNameChanged<Script>(const Script &entry, const QString &oldName) { emit scriptNameChanged(entry, oldName); }
|
||||
template<> void ProjectTreeModel::emitNameChanged<Font>(const Font &entry, const QString &oldName) { emit fontNameChanged(entry, oldName); }
|
||||
template<> void ProjectTreeModel::emitNameChanged<TimeLine>(const TimeLine &entry, const QString &oldName) { emit timeLineNameChanged(entry, oldName); }
|
||||
template<> void ProjectTreeModel::emitNameChanged<Object>(const Object &entry, const QString &oldName) { emit objectNameChanged(entry, oldName); }
|
||||
template<> void ProjectTreeModel::emitNameChanged<Room>(const Room &entry, const QString &oldName) { emit roomNameChanged(entry, oldName); }
|
||||
template<> QString ProjectTreeModel::nameTempateFor<Sprite>() { return QStringLiteral("sprite%0"); }
|
||||
template<> QString ProjectTreeModel::nameTempateFor<Sound>() { return QStringLiteral("sound%0"); }
|
||||
template<> QString ProjectTreeModel::nameTempateFor<Background>() { return QStringLiteral("background%0"); }
|
||||
template<> QString ProjectTreeModel::nameTempateFor<Path>() { return QStringLiteral("path%0"); }
|
||||
template<> QString ProjectTreeModel::nameTempateFor<Script>() { return QStringLiteral("script%0"); }
|
||||
template<> QString ProjectTreeModel::nameTempateFor<Font>() { return QStringLiteral("font%0"); }
|
||||
template<> QString ProjectTreeModel::nameTempateFor<TimeLine>() { return QStringLiteral("timeline%0"); }
|
||||
template<> QString ProjectTreeModel::nameTempateFor<Object>() { return QStringLiteral("object%0"); }
|
||||
template<> QString ProjectTreeModel::nameTempateFor<Room>() { return QStringLiteral("room%0"); }
|
||||
@@ -0,0 +1,152 @@
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
|
||||
#include <optional>
|
||||
|
||||
struct ProjectContainer;
|
||||
struct Sprite;
|
||||
struct Sound;
|
||||
struct Background;
|
||||
struct Path;
|
||||
struct Script;
|
||||
struct Font;
|
||||
struct TimeLine;
|
||||
struct Object;
|
||||
struct Room;
|
||||
|
||||
class ProjectTreeModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class NodeType {
|
||||
Root = 12,
|
||||
Sprite,
|
||||
Sound,
|
||||
Background,
|
||||
Path,
|
||||
Script,
|
||||
Font,
|
||||
TimeLine,
|
||||
Object,
|
||||
Room,
|
||||
};
|
||||
|
||||
explicit ProjectTreeModel(QObject *parent = nullptr);
|
||||
explicit ProjectTreeModel(ProjectContainer &project, QObject *parent = nullptr);
|
||||
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
|
||||
QModelIndex parent(const QModelIndex &child) const override;
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
int columnCount(const QModelIndex &parent) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||
bool insertRows(int row, int count, const QModelIndex &parent) override;
|
||||
bool removeRows(int row, int count, const QModelIndex &parent) override;
|
||||
|
||||
ProjectContainer *project() { return m_project; }
|
||||
const ProjectContainer *project() const { return m_project; }
|
||||
void setProject(ProjectContainer *project);
|
||||
|
||||
NodeType nodeType(const QModelIndex &index) const;
|
||||
|
||||
template<typename T> QModelIndex rootFor() const;
|
||||
|
||||
QModelIndex gameInformationRoot() const;
|
||||
QModelIndex globalGameSettingsRoot() const;
|
||||
QModelIndex extensionPackagesRoot() const;
|
||||
|
||||
template<typename T> T *get(const QModelIndex &index);
|
||||
template<typename T> const T *get(const QModelIndex &index) const;
|
||||
|
||||
template<typename T> T *create();
|
||||
|
||||
template<typename T> bool remove(const T &entry);
|
||||
|
||||
template<typename T> bool rename(const T &entry, const QString &newName);
|
||||
|
||||
template<typename T> static constexpr ProjectTreeModel::NodeType nodeTypeFor() = delete;
|
||||
|
||||
bool setSpritePixmaps(const Sprite &sprite, std::vector<QPixmap> &&pixmaps);
|
||||
|
||||
bool setBackgroundPixmap(const Background &background, QPixmap &&pixmap);
|
||||
|
||||
signals:
|
||||
void errorOccured(const QString &message);
|
||||
|
||||
void spriteCreated(const Sprite &sprite);
|
||||
void soundCreated(const Sound &sound);
|
||||
void backgroundCreated(const Background &background);
|
||||
void pathCreated(const Path &path);
|
||||
void scriptCreated(const Script &script);
|
||||
void fontCreated(const Font &font);
|
||||
void timeLineCreated(const TimeLine &font);
|
||||
void objectCreated(const Object &font);
|
||||
void roomCreated(const Room &font);
|
||||
|
||||
void spriteAboutToBeRemoved(const Sprite &sprite);
|
||||
void soundAboutToBeRemoved(const Sound &sound);
|
||||
void backgroundAboutToBeRemoved(const Background &background);
|
||||
void pathAboutToBeRemoved(const Path &path);
|
||||
void scriptAboutToBeRemoved(const Script &script);
|
||||
void fontAboutToBeRemoved(const Font &font);
|
||||
void timeLineAboutToBeRemoved(const TimeLine &font);
|
||||
void objectAboutToBeRemoved(const Object &font);
|
||||
void roomAboutToBeRemoved(const Room &font);
|
||||
|
||||
void spriteNameChanged(const Sprite &sprite, const QString &oldName);
|
||||
void soundNameChanged(const Sound &sound, const QString &oldName);
|
||||
void backgroundNameChanged(const Background &background, const QString &oldName);
|
||||
void pathNameChanged(const Path &path, const QString &oldName);
|
||||
void scriptNameChanged(const Script &script, const QString &oldName);
|
||||
void fontNameChanged(const Font &font, const QString &oldName);
|
||||
void timeLineNameChanged(const TimeLine &font, const QString &oldName);
|
||||
void objectNameChanged(const Object &font, const QString &oldName);
|
||||
void roomNameChanged(const Room &font, const QString &oldName);
|
||||
|
||||
void spritePixmapsChanged(const Sprite &sprite);
|
||||
|
||||
void backgroundPixmapChanged(const Background &background);
|
||||
|
||||
void objectSpriteNameChanged(const Object &object, const QString &oldSpriteName);
|
||||
|
||||
private:
|
||||
template<typename T> QVariant dataFor(const QModelIndex &index, int role) const;
|
||||
template<typename T> QVariant iconFor(const T &entry) const;
|
||||
template<typename T> bool setDataFor(const QModelIndex &index, const QVariant &value, int role);
|
||||
template<typename T> std::optional<bool> insertRowsFor(int row, int count, const QModelIndex &parent);
|
||||
template<typename T> std::optional<bool> removeRowsFor(int row, int count, const QModelIndex &parent);
|
||||
template<typename T> static QString getFreeNameFor(const std::list<T> &container);
|
||||
|
||||
template<typename T> void emitCreated(const T &entry);
|
||||
template<typename T> void onBeforeRemove(const T &entry);
|
||||
template<typename T> void emitAboutToBeRemoved(const T &entry);
|
||||
template<typename T> void onBeforeRename(const T &entry, const QString &newName);
|
||||
template<typename T> void onAfterRename(const T &entry, const QString &oldName);
|
||||
template<typename T> void emitNameChanged(const T &entry, const QString &oldName);
|
||||
template<typename T> static QString nameTempateFor();
|
||||
|
||||
ProjectContainer *m_project{};
|
||||
};
|
||||
|
||||
template<> constexpr ProjectTreeModel::NodeType ProjectTreeModel::nodeTypeFor<Sprite>() { return ProjectTreeModel::NodeType::Sprite; }
|
||||
template<> constexpr ProjectTreeModel::NodeType ProjectTreeModel::nodeTypeFor<Sound>() { return ProjectTreeModel::NodeType::Sound; }
|
||||
template<> constexpr ProjectTreeModel::NodeType ProjectTreeModel::nodeTypeFor<Background>() { return ProjectTreeModel::NodeType::Background; }
|
||||
template<> constexpr ProjectTreeModel::NodeType ProjectTreeModel::nodeTypeFor<Path>() { return ProjectTreeModel::NodeType::Path; }
|
||||
template<> constexpr ProjectTreeModel::NodeType ProjectTreeModel::nodeTypeFor<Script>() { return ProjectTreeModel::NodeType::Script; }
|
||||
template<> constexpr ProjectTreeModel::NodeType ProjectTreeModel::nodeTypeFor<Font>() { return ProjectTreeModel::NodeType::Font; }
|
||||
template<> constexpr ProjectTreeModel::NodeType ProjectTreeModel::nodeTypeFor<TimeLine>() { return ProjectTreeModel::NodeType::TimeLine; }
|
||||
template<> constexpr ProjectTreeModel::NodeType ProjectTreeModel::nodeTypeFor<Object>() { return ProjectTreeModel::NodeType::Object; }
|
||||
template<> constexpr ProjectTreeModel::NodeType ProjectTreeModel::nodeTypeFor<Room>() { return ProjectTreeModel::NodeType::Room; }
|
||||
|
||||
template<> QModelIndex ProjectTreeModel::rootFor<Sprite>() const;
|
||||
template<> QModelIndex ProjectTreeModel::rootFor<Sound>() const;
|
||||
template<> QModelIndex ProjectTreeModel::rootFor<Background>() const;
|
||||
template<> QModelIndex ProjectTreeModel::rootFor<Path>() const;
|
||||
template<> QModelIndex ProjectTreeModel::rootFor<Script>() const;
|
||||
template<> QModelIndex ProjectTreeModel::rootFor<Font>() const;
|
||||
template<> QModelIndex ProjectTreeModel::rootFor<TimeLine>() const;
|
||||
template<> QModelIndex ProjectTreeModel::rootFor<Object>() const;
|
||||
template<> QModelIndex ProjectTreeModel::rootFor<Room>() const;
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "spritesmodel.h"
|
||||
|
||||
#include <QPixmap>
|
||||
#include <QDebug>
|
||||
|
||||
SpritesModel::SpritesModel(const std::vector<QPixmap> &pixmaps, QObject *parent) :
|
||||
QAbstractListModel{parent},
|
||||
m_pixmaps{pixmaps}
|
||||
{
|
||||
}
|
||||
|
||||
int SpritesModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return m_pixmaps.size();
|
||||
}
|
||||
|
||||
QVariant SpritesModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (index.column() != 0)
|
||||
return {};
|
||||
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
return tr("image %0").arg(index.row());
|
||||
case Qt::DecorationRole:
|
||||
if (index.row() < 0 || std::size_t(index.row()) >= m_pixmaps.size())
|
||||
{
|
||||
qWarning() << "invalid row" << index.row();
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto &pixmap = m_pixmaps.at(index.row());
|
||||
if (pixmap.isNull())
|
||||
{
|
||||
QPixmap pixmap{32, 32};
|
||||
pixmap.fill(Qt::white);
|
||||
return pixmap;
|
||||
}
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
const QPixmap &SpritesModel::pixmap(const QModelIndex &index) const
|
||||
{
|
||||
if (index.row() < 0 || std::size_t(index.row()) >= m_pixmaps.size())
|
||||
qFatal("index out of range");
|
||||
|
||||
return m_pixmaps.at(index.row());
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QPixmap>
|
||||
|
||||
#include <vector>
|
||||
|
||||
class SpritesModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SpritesModel(const std::vector<QPixmap> &pixmaps, QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
|
||||
const QPixmap &pixmap(const QModelIndex &index) const;
|
||||
|
||||
using QAbstractListModel::beginResetModel;
|
||||
using QAbstractListModel::endResetModel;
|
||||
|
||||
private:
|
||||
const std::vector<QPixmap> &m_pixmaps;
|
||||
};
|
||||
@@ -0,0 +1,232 @@
|
||||
#include "timelinemomentsmodel.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
TimelineMomentsModel::TimelineMomentsModel(TimeLine::moments_container_t &moments, QObject *parent) :
|
||||
QAbstractListModel{parent},
|
||||
m_moments{moments}
|
||||
{
|
||||
}
|
||||
|
||||
int TimelineMomentsModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return m_moments.size();
|
||||
}
|
||||
|
||||
QVariant TimelineMomentsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
Q_UNUSED(index)
|
||||
|
||||
if (index.row() < 0 || std::size_t(index.row()) >= m_moments.size())
|
||||
{
|
||||
qWarning() << "row out of bounds" << index.row();
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto &pair = *std::next(std::cbegin(m_moments), index.row());
|
||||
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
return tr("Step %0").arg(pair.first);
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool TimelineMomentsModel::removeRows(int row, int count, const QModelIndex &parent)
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
|
||||
if (row < 0 || std::size_t(row) >= m_moments.size())
|
||||
{
|
||||
qWarning() << "unexpected row" << row;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count < 0 || std::size_t(count) > m_moments.size() - row)
|
||||
{
|
||||
qWarning() << "unexpected row+count" << count << row;
|
||||
return false;
|
||||
}
|
||||
|
||||
beginRemoveRows({}, row, row + count - 1);
|
||||
auto begin = std::next(std::begin(m_moments), row);
|
||||
auto end = std::next(begin, count);
|
||||
m_moments.erase(begin, end);
|
||||
endRemoveRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TimelineMomentsModel::addMoment(TimeLine::moment_key_t moment)
|
||||
{
|
||||
if (m_moments.contains(moment))
|
||||
{
|
||||
qWarning() << moment << "duplicate";
|
||||
return false;
|
||||
}
|
||||
|
||||
// temporary copy to find row before inserting, as its needed for beginInsertRows()
|
||||
auto tempMoments = m_moments;
|
||||
|
||||
const auto &tempInsertResult = tempMoments.insert(std::make_pair(moment, ActionsContainer{}));
|
||||
if (!tempInsertResult.second)
|
||||
{
|
||||
qWarning() << "temp inserting failed!";
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto tempNewRow = std::distance(std::begin(tempMoments), tempInsertResult.first);
|
||||
|
||||
|
||||
beginInsertRows({}, tempNewRow, tempNewRow);
|
||||
|
||||
const auto &insertResult = m_moments.insert(std::make_pair(moment, ActionsContainer{}));
|
||||
if (!insertResult.second)
|
||||
{
|
||||
qWarning() << "inserting failed!";
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto newRow = std::distance(std::begin(m_moments), insertResult.first);
|
||||
Q_ASSERT(tempNewRow == newRow);
|
||||
|
||||
endInsertRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TimelineMomentsModel::changeMoment(TimeLine::moment_key_t moment, TimeLine::moment_key_t newMoment)
|
||||
{
|
||||
const auto iter = m_moments.find(moment);
|
||||
if (iter == std::cend(m_moments))
|
||||
{
|
||||
qWarning() << moment << "not found";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (moment == newMoment)
|
||||
{
|
||||
qWarning() << "same moment again";
|
||||
return true;
|
||||
}
|
||||
|
||||
auto container = std::move(iter->second);
|
||||
|
||||
const auto oldRow = std::distance(std::begin(m_moments), iter);
|
||||
|
||||
beginRemoveRows({}, oldRow, oldRow);
|
||||
m_moments.erase(iter);
|
||||
endRemoveRows();
|
||||
|
||||
// temporary copy to find row before inserting, as its needed for beginInsertRows()
|
||||
auto tempMoments = m_moments;
|
||||
|
||||
const auto &tempInsertResult = tempMoments.insert(std::make_pair(newMoment, ActionsContainer{}));
|
||||
if (!tempInsertResult.second)
|
||||
{
|
||||
qWarning() << "temp inserting failed!";
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto tempNewRow = std::distance(std::begin(tempMoments), tempInsertResult.first);
|
||||
|
||||
|
||||
beginInsertRows({}, tempNewRow, tempNewRow);
|
||||
|
||||
const auto &insertResult = m_moments.insert(std::make_pair(newMoment, std::move(container)));
|
||||
if (!insertResult.second)
|
||||
{
|
||||
qWarning() << "inserting failed!";
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto newRow = std::distance(std::begin(m_moments), insertResult.first);
|
||||
Q_ASSERT(tempNewRow == newRow);
|
||||
|
||||
endInsertRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TimelineMomentsModel::removeMoment(TimeLine::moment_key_t moment)
|
||||
{
|
||||
const auto iter = m_moments.find(moment);
|
||||
if (iter == std::cend(m_moments))
|
||||
{
|
||||
qWarning() << moment << "not found";
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto row = std::distance(std::begin(m_moments), iter);
|
||||
|
||||
beginRemoveRows({}, row, row);
|
||||
m_moments.erase(iter);
|
||||
endRemoveRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TimelineMomentsModel::clearMoments()
|
||||
{
|
||||
if (m_moments.empty())
|
||||
{
|
||||
qWarning() << "moments already empty";
|
||||
return true;
|
||||
}
|
||||
|
||||
beginRemoveRows({}, 0, m_moments.size() - 1);
|
||||
m_moments.clear();
|
||||
endRemoveRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::pair<const TimeLine::moment_key_t, ActionsContainer> *TimelineMomentsModel::getMoment(const QModelIndex &index)
|
||||
{
|
||||
if (!index.isValid())
|
||||
{
|
||||
qWarning() << "unexpected invalid index";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return getMoment(index.row());
|
||||
}
|
||||
|
||||
const std::pair<const TimeLine::moment_key_t, ActionsContainer> *TimelineMomentsModel::getMoment(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
{
|
||||
qWarning() << "unexpected invalid index";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return getMoment(index.row());
|
||||
}
|
||||
|
||||
std::pair<const TimeLine::moment_key_t, ActionsContainer> *TimelineMomentsModel::getMoment(int row)
|
||||
{
|
||||
if (row < 0 || std::size_t(row) >= m_moments.size())
|
||||
{
|
||||
qWarning() << "unexpected row" << row;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &*std::next(std::begin(m_moments), row);
|
||||
}
|
||||
|
||||
const std::pair<const TimeLine::moment_key_t, ActionsContainer> *TimelineMomentsModel::getMoment(int row) const
|
||||
{
|
||||
if (row < 0 || std::size_t(row) >= m_moments.size())
|
||||
{
|
||||
qWarning() << "unexpected row" << row;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &*std::next(std::cbegin(m_moments), row);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
#include "projectcontainer.h"
|
||||
|
||||
class TimelineMomentsModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TimelineMomentsModel(TimeLine::moments_container_t &moments, QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex{}) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex{}) override;
|
||||
|
||||
bool addMoment(TimeLine::moment_key_t moment);
|
||||
bool changeMoment(TimeLine::moment_key_t moment, TimeLine::moment_key_t newMoment);
|
||||
bool removeMoment(TimeLine::moment_key_t moment);
|
||||
bool clearMoments();
|
||||
|
||||
std::pair<const TimeLine::moment_key_t, ActionsContainer> *getMoment(const QModelIndex &index);
|
||||
const std::pair<const TimeLine::moment_key_t, ActionsContainer> *getMoment(const QModelIndex &index) const;
|
||||
std::pair<const TimeLine::moment_key_t, ActionsContainer> *getMoment(int row);
|
||||
const std::pair<const TimeLine::moment_key_t, ActionsContainer> *getMoment(int row) const;
|
||||
|
||||
private:
|
||||
TimeLine::moments_container_t &m_moments;
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "triggersmodel.h"
|
||||
|
||||
#include "projectcontainer.h"
|
||||
|
||||
TriggersModel::TriggersModel(ProjectContainer &project, QObject *parent) :
|
||||
QAbstractListModel{parent},
|
||||
m_project{project}
|
||||
{
|
||||
}
|
||||
|
||||
int TriggersModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return 0;
|
||||
}
|
||||
|
||||
QVariant TriggersModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
Q_UNUSED(index)
|
||||
Q_UNUSED(role)
|
||||
return {};
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
struct ProjectContainer;
|
||||
|
||||
class TriggersModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TriggersModel(ProjectContainer &project, QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
|
||||
private:
|
||||
ProjectContainer &m_project;
|
||||
};
|
||||
Reference in New Issue
Block a user