Clang compatibility improved and variant support for serializer

This commit is contained in:
2024-01-02 19:54:25 +01:00
parent ac3f934118
commit ce791f5ff3
6 changed files with 497 additions and 456 deletions

View File

@ -165,6 +165,60 @@ MainWindow::MainWindow(const QString &filePath, QWidget *parent) :
loadFile(m_filePath); loadFile(m_filePath);
} }
template<>
std::map<Sprite*, QMdiSubWindow*> &MainWindow::propertyWindowsFor<Sprite>()
{
return m_spritePropertiesWindows;
}
template<>
std::map<Sound*, QMdiSubWindow*> &MainWindow::propertyWindowsFor<Sound>()
{
return m_soundPropertiesWindows;
}
template<>
std::map<Background*, QMdiSubWindow*> &MainWindow::propertyWindowsFor<Background>()
{
return m_backgroundPropertiesWindows;
}
template<>
std::map<Path*, QMdiSubWindow*> &MainWindow::propertyWindowsFor<Path>()
{
return m_pathPropertiesWindows;
}
template<>
std::map<Script*, QMdiSubWindow*> &MainWindow::propertyWindowsFor<Script>()
{
return m_scriptPropertiesWindows;
}
template<>
std::map<Font*, QMdiSubWindow*> &MainWindow::propertyWindowsFor<Font>()
{
return m_fontPropertiesWindows;
}
template<>
std::map<TimeLine*, QMdiSubWindow*> &MainWindow::propertyWindowsFor<TimeLine>()
{
return m_timeLinePropertiesWindows;
}
template<>
std::map<Object*, QMdiSubWindow*> &MainWindow::propertyWindowsFor<Object>()
{
return m_objectPropertiesWindows;
}
template<>
std::map<Room*, QMdiSubWindow*> &MainWindow::propertyWindowsFor<Room>()
{
return m_roomPropertiesWindows;
}
template<typename T> template<typename T>
void MainWindow::openPropertiesWindowFor(T &entry) void MainWindow::openPropertiesWindowFor(T &entry)
{ {
@ -962,57 +1016,3 @@ void MainWindow::modelAboutToBeResetFor()
delete pair.second; delete pair.second;
propertyWindows.clear(); propertyWindows.clear();
} }
template<>
std::map<Sprite*, QMdiSubWindow*> &MainWindow::propertyWindowsFor<Sprite>()
{
return m_spritePropertiesWindows;
}
template<>
std::map<Sound*, QMdiSubWindow*> &MainWindow::propertyWindowsFor<Sound>()
{
return m_soundPropertiesWindows;
}
template<>
std::map<Background*, QMdiSubWindow*> &MainWindow::propertyWindowsFor<Background>()
{
return m_backgroundPropertiesWindows;
}
template<>
std::map<Path*, QMdiSubWindow*> &MainWindow::propertyWindowsFor<Path>()
{
return m_pathPropertiesWindows;
}
template<>
std::map<Script*, QMdiSubWindow*> &MainWindow::propertyWindowsFor<Script>()
{
return m_scriptPropertiesWindows;
}
template<>
std::map<Font*, QMdiSubWindow*> &MainWindow::propertyWindowsFor<Font>()
{
return m_fontPropertiesWindows;
}
template<>
std::map<TimeLine*, QMdiSubWindow*> &MainWindow::propertyWindowsFor<TimeLine>()
{
return m_timeLinePropertiesWindows;
}
template<>
std::map<Object*, QMdiSubWindow*> &MainWindow::propertyWindowsFor<Object>()
{
return m_objectPropertiesWindows;
}
template<>
std::map<Room*, QMdiSubWindow*> &MainWindow::propertyWindowsFor<Room>()
{
return m_roomPropertiesWindows;
}

View File

@ -20,6 +20,11 @@ public:
explicit MainWindow(const QString &filePath, QWidget *parent = nullptr); explicit MainWindow(const QString &filePath, QWidget *parent = nullptr);
~MainWindow(); ~MainWindow();
private:
template<typename T>
std::map<T*, QMdiSubWindow*> &propertyWindowsFor();
public:
template<typename T> template<typename T>
void openPropertiesWindowFor(T &entry); void openPropertiesWindowFor(T &entry);
@ -77,9 +82,6 @@ private:
template<typename T, typename ...Targs> template<typename T, typename ...Targs>
void openOrActivateWindow(QMdiSubWindow * &ptr, Targs &&...args); void openOrActivateWindow(QMdiSubWindow * &ptr, Targs &&...args);
template<typename T>
std::map<T*, QMdiSubWindow*> &propertyWindowsFor();
template<typename T> template<typename T>
bool doubleClickedFor(const QModelIndex &index); bool doubleClickedFor(const QModelIndex &index);

View File

@ -34,7 +34,7 @@ QVariant ActionsContainerModel::data(const QModelIndex &index, int role) const
return {}; return {};
} }
const auto &action = m_actionsContainer->at(index.row()); //const auto &action = m_actionsContainer->at(index.row());
switch (role) switch (role)
{ {

View File

@ -39,6 +39,389 @@ ProjectTreeModel::ProjectTreeModel(ProjectContainer &project, QObject *parent) :
{ {
} }
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<> void ProjectTreeModel::onBeforeRemove<Object>(const Object &object)
{
for (Object &obj : m_project->objects)
{
if (!obj.parentName.isEmpty() && obj.parentName == object.name)
obj.parentName.clear();
obj.collisionEvents.erase(object.name);
}
for (Room &room : m_project->rooms)
for (auto iter = std::begin(room.objects); iter != std::end(room.objects); )
if (iter->objectName == object.name)
iter = room.objects.erase(iter);
else
iter++;
}
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 &object : m_project->objects)
{
if (object.spriteName != oldName)
continue;
auto oldSpriteName = std::move(object.spriteName);
object.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(object, std::move(oldSpriteName));
}
}
template<> void ProjectTreeModel::onBeforeRename<Object>(const Object &object, const QString &newName)
{
for (Object &obj : m_project->objects)
{
if (!obj.parentName.isEmpty() && obj.parentName == object.name)
obj.parentName = newName;
if (const auto iter = obj.collisionEvents.find(object.name); iter != std::end(obj.collisionEvents))
{
auto node = obj.collisionEvents.extract(iter);
node.key() = newName;
obj.collisionEvents.insert(std::move(node));
}
}
for (auto &room : m_project->rooms)
{
for (auto &obj : room.objects)
{
if (obj.objectName != object.name)
continue;
obj.objectName = object.name;
}
}
}
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"); }
QModelIndex ProjectTreeModel::index(int row, int column, const QModelIndex &parent) const QModelIndex ProjectTreeModel::index(int row, int column, const QModelIndex &parent) const
{ {
if (!parent.isValid()) if (!parent.isValid())
@ -701,386 +1084,3 @@ bool ProjectTreeModel::setObjectSpriteName(const Object &object, QString &&sprit
return true; 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<> void ProjectTreeModel::onBeforeRemove<Object>(const Object &object)
{
for (Object &obj : m_project->objects)
{
if (!obj.parentName.isEmpty() && obj.parentName == object.name)
obj.parentName.clear();
obj.collisionEvents.erase(object.name);
}
for (Room &room : m_project->rooms)
for (auto iter = std::begin(room.objects); iter != std::end(room.objects); )
if (iter->objectName == object.name)
iter = room.objects.erase(iter);
else
iter++;
}
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 &object : m_project->objects)
{
if (object.spriteName != oldName)
continue;
auto oldSpriteName = std::move(object.spriteName);
object.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(object, std::move(oldSpriteName));
}
}
template<> void ProjectTreeModel::onBeforeRename<Object>(const Object &object, const QString &newName)
{
for (Object &obj : m_project->objects)
{
if (!obj.parentName.isEmpty() && obj.parentName == object.name)
obj.parentName = newName;
if (const auto iter = obj.collisionEvents.find(object.name); iter != std::end(obj.collisionEvents))
{
auto node = obj.collisionEvents.extract(iter);
node.key() = newName;
obj.collisionEvents.insert(std::move(node));
}
}
for (auto &room : m_project->rooms)
{
for (auto &obj : room.objects)
{
if (obj.objectName != object.name)
continue;
obj.objectName = object.name;
}
}
}
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"); }

View File

@ -36,6 +36,23 @@ public:
explicit ProjectTreeModel(QObject *parent = nullptr); explicit ProjectTreeModel(QObject *parent = nullptr);
explicit ProjectTreeModel(ProjectContainer &project, QObject *parent = nullptr); explicit ProjectTreeModel(ProjectContainer &project, QObject *parent = nullptr);
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();
public:
QModelIndex index(int row, int column, const QModelIndex &parent) const override; QModelIndex index(int row, int column, const QModelIndex &parent) const override;
QModelIndex parent(const QModelIndex &child) const override; QModelIndex parent(const QModelIndex &child) const override;
int rowCount(const QModelIndex &parent) const override; int rowCount(const QModelIndex &parent) const override;
@ -117,21 +134,6 @@ signals:
void objectSpriteNameChanged(const Object &object, const QString &oldSpriteName); void objectSpriteNameChanged(const Object &object, const QString &oldSpriteName);
private: 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{}; ProjectContainer *m_project{};
}; };

View File

@ -118,6 +118,43 @@ QDataStream &operator>>(QDataStream &ds, std::map<Tkey, Tvalue> &map)
return ds; return ds;
} }
template<typename ...T>
QDataStream &operator<<(QDataStream &ds, const std::variant<T...> &variant)
{
int index = variant.index();
ds << index;
using func_t = void(QDataStream&, const std::variant<T...> &);
static constexpr func_t *funcs[] = {
[](QDataStream& ds, const std::variant<T...> &variant) {
ds << std::get<T>(variant);
}...
};
funcs[index](ds, variant);
return ds;
}
template<typename ...T>
QDataStream &operator>>(QDataStream &ds, std::variant<T...> &variant)
{
int index;
ds >> index;
using func_t = std::variant<T...> (QDataStream&);
static constexpr func_t *funcs[] = {
[](QDataStream& ds) -> std::variant<T...> {
T value;
ds >> value;
return value;
}...
};
variant = funcs[index](ds);
return ds;
}
QDataStream &operator<<(QDataStream &ds, const GlobalGameSettings &globalGameSettings) QDataStream &operator<<(QDataStream &ds, const GlobalGameSettings &globalGameSettings)
{ {
Q_UNUSED(globalGameSettings); Q_UNUSED(globalGameSettings);