From 42575e5d5a4dcd76de76fd2d22eb367b7c457724 Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Fri, 29 Dec 2023 17:48:21 +0100 Subject: [PATCH] Add collision events and fix a few bugs when objects are renamed/deleted --- src/editor/dialogs/addeventdialog.cpp | 29 +- src/editor/dialogs/addeventdialog.h | 11 +- src/editor/dialogs/addeventdialog.ui | 6 +- src/editor/dialogs/objectpropertiesdialog.cpp | 32 +- src/editor/dialogs/objectpropertiesdialog.h | 1 + src/editor/dialogs/roompropertiesdialog.cpp | 36 +- src/editor/dialogs/roompropertiesdialog.h | 2 +- src/editor/icons/event-collision.png | Bin 0 -> 8081 bytes src/editor/models/objecteventsmodel.cpp | 329 +++++++++++++++--- src/editor/models/objecteventsmodel.h | 21 +- src/editor/models/projecttreemodel.cpp | 36 +- src/editor/resources_editor.qrc | 1 + src/projectcontainer.cpp | 6 +- src/projectcontainer.h | 3 + 14 files changed, 416 insertions(+), 97 deletions(-) create mode 100644 src/editor/icons/event-collision.png diff --git a/src/editor/dialogs/addeventdialog.cpp b/src/editor/dialogs/addeventdialog.cpp index e99610b..e994f9f 100644 --- a/src/editor/dialogs/addeventdialog.cpp +++ b/src/editor/dialogs/addeventdialog.cpp @@ -2,9 +2,13 @@ #include "ui_addeventdialog.h" #include +#include -AddEventDialog::AddEventDialog(QWidget *parent) : +#include "models/projecttreemodel.h" + +AddEventDialog::AddEventDialog(ProjectTreeModel &projectModel, QWidget *parent) : QDialog{parent}, + m_projectModel{projectModel}, m_ui{std::make_unique()} { m_ui->setupUi(this); @@ -27,6 +31,29 @@ AddEventDialog::AddEventDialog(QWidget *parent) : this, [this](){ m_eventType = Object::EventType::Draw; accept(); }); connect(m_ui->pushButtonStep, &QAbstractButton::clicked, this, [this](){ m_eventType = Object::EventType::Step; accept(); }); + + auto menu = new QMenu; + connect(menu, &QMenu::aboutToShow, menu, [this,menu](){ + for (const Object &object : m_projectModel.project()->objects) + { + QIcon icon; + if (!object.spriteName.isEmpty()) + { + const auto &sprites = m_projectModel.project()->sprites; + const auto iter = std::find_if(std::cbegin(sprites), std::cend(sprites), + [&](const Sprite &sprite){ return sprite.name == object.spriteName; }); + if (iter != std::cend(sprites)) + { + if (!iter->pixmaps.empty()) + icon = iter->pixmaps.front(); + } + } + menu->addAction(icon, object.name, this, [this,&object](){ + m_eventType = object.name; accept(); + }); + } + }, Qt::SingleShotConnection); + m_ui->pushButtonCollision->setMenu(menu); } AddEventDialog::~AddEventDialog() = default; diff --git a/src/editor/dialogs/addeventdialog.h b/src/editor/dialogs/addeventdialog.h index 3b88fd6..ce4c23b 100644 --- a/src/editor/dialogs/addeventdialog.h +++ b/src/editor/dialogs/addeventdialog.h @@ -3,26 +3,31 @@ #include #include +#include #include "projectcontainer.h" namespace Ui { class AddEventDialog; } +class ProjectTreeModel; + class AddEventDialog : public QDialog { Q_OBJECT public: - explicit AddEventDialog(QWidget *parent = nullptr); + explicit AddEventDialog(ProjectTreeModel &projectModel, QWidget *parent = nullptr); ~AddEventDialog(); - Object::EventType eventType() const { return *m_eventType; } + const std::optional> &eventType() const { return m_eventType; } void accept() override; void reject() override; private: + ProjectTreeModel &m_projectModel; + const std::unique_ptr m_ui; - std::optional m_eventType; + std::optional> m_eventType; }; diff --git a/src/editor/dialogs/addeventdialog.ui b/src/editor/dialogs/addeventdialog.ui index bfd4e6b..3d4e022 100644 --- a/src/editor/dialogs/addeventdialog.ui +++ b/src/editor/dialogs/addeventdialog.ui @@ -7,7 +7,7 @@ 0 0 265 - 221 + 238 @@ -82,6 +82,10 @@ C&ollision + + + :/qtgameengine/icons/event-collision.png:/qtgameengine/icons/event-collision.png + diff --git a/src/editor/dialogs/objectpropertiesdialog.cpp b/src/editor/dialogs/objectpropertiesdialog.cpp index ed1fae6..def3293 100644 --- a/src/editor/dialogs/objectpropertiesdialog.cpp +++ b/src/editor/dialogs/objectpropertiesdialog.cpp @@ -19,7 +19,8 @@ ObjectPropertiesDialog::ObjectPropertiesDialog(Object &object, ProjectTreeModel m_projectModel{projectModel}, m_mainWindow{mainWindow}, m_events{m_object.events}, - m_eventsModel{std::make_unique(m_events)}, + m_collisionEvents{m_object.collisionEvents}, + m_eventsModel{std::make_unique(m_events, m_collisionEvents)}, m_menuSprites{new QMenu{this}}, m_spriteName{object.spriteName} { @@ -132,6 +133,7 @@ void ObjectPropertiesDialog::accept() m_object.depth = m_ui->spinBoxDepth->value(); m_object.persistent = m_ui->checkBoxPersistent->isChecked(); m_object.events = std::move(m_events); + m_object.collisionEvents = std::move(m_collisionEvents); QDialog::accept(); } @@ -204,12 +206,11 @@ void ObjectPropertiesDialog::showInformation() void ObjectPropertiesDialog::addEvent() { - AddEventDialog dialog{this}; + AddEventDialog dialog{m_projectModel, this}; if (dialog.exec() == QDialog::Accepted) - { - if (!m_eventsModel->addEvent(dialog.eventType())) - QMessageBox::warning(this, tr("Could not add Event!"), tr("Could not add Event!")); - } + if (const auto &eventType = dialog.eventType()) + if (!m_eventsModel->addEvent(*eventType)) + QMessageBox::warning(this, tr("Could not add Event!"), tr("Could not add Event!")); } void ObjectPropertiesDialog::deleteEvent() @@ -235,12 +236,13 @@ void ObjectPropertiesDialog::replaceEvent() if (!event) return; - AddEventDialog dialog{this}; + std::variant x = event->first; + + AddEventDialog dialog{m_projectModel, this}; if (dialog.exec() == QDialog::Accepted) - { - if (!m_eventsModel->changeEvent(event->first, dialog.eventType())) + if (const auto &eventType = dialog.eventType()) + if (!m_eventsModel->changeEvent(event->first, *eventType)) QMessageBox::warning(this, tr("Could not change Event!"), tr("Could not change Event!")); - } } void ObjectPropertiesDialog::duplicateEvent() @@ -329,7 +331,7 @@ void ObjectPropertiesDialog::currentEventChanged(const QModelIndex &index) if (index.isValid()) { if (auto event = m_eventsModel->getEvent(index)) - m_ui->actionsWidget->setActionsContainer(&event->second); + m_ui->actionsWidget->setActionsContainer(&event->second.get()); else goto none; } @@ -343,13 +345,13 @@ none: void ObjectPropertiesDialog::eventsContextMenuRequested(const QPoint &pos) { const auto index = m_ui->listViewEvents->indexAt(pos); - auto event = index.isValid() ? m_eventsModel->getEvent(index) : nullptr; + auto event = index.isValid() ? m_eventsModel->getEvent(index) : std::nullopt; QMenu menu{this}; menu.addAction(tr("&Add Event"), this, &ObjectPropertiesDialog::addEvent); - menu.addAction(tr("&Change Event"), this, &ObjectPropertiesDialog::replaceEvent)->setEnabled(event); - menu.addAction(tr("&Duplicate Event"), this, &ObjectPropertiesDialog::duplicateEvent)->setEnabled(event); - menu.addAction(tr("D&elete Event"), this, &ObjectPropertiesDialog::deleteEvent)->setEnabled(event); + menu.addAction(tr("&Change Event"), this, &ObjectPropertiesDialog::replaceEvent)->setEnabled(event.has_value()); + menu.addAction(tr("&Duplicate Event"), this, &ObjectPropertiesDialog::duplicateEvent)->setEnabled(event.has_value()); + menu.addAction(tr("D&elete Event"), this, &ObjectPropertiesDialog::deleteEvent)->setEnabled(event.has_value()); menu.exec(m_ui->listViewEvents->viewport()->mapToGlobal(pos)); } diff --git a/src/editor/dialogs/objectpropertiesdialog.h b/src/editor/dialogs/objectpropertiesdialog.h index 8f6fc61..c470629 100644 --- a/src/editor/dialogs/objectpropertiesdialog.h +++ b/src/editor/dialogs/objectpropertiesdialog.h @@ -60,6 +60,7 @@ private: MainWindow * const m_mainWindow; Object::events_container_t m_events; + Object::collision_events_container_t m_collisionEvents; const std::unique_ptr m_eventsModel; diff --git a/src/editor/dialogs/roompropertiesdialog.cpp b/src/editor/dialogs/roompropertiesdialog.cpp index 1bc89bd..41e78d1 100644 --- a/src/editor/dialogs/roompropertiesdialog.cpp +++ b/src/editor/dialogs/roompropertiesdialog.cpp @@ -343,29 +343,35 @@ void RoomPropertiesDialog::spritePixmapsChanged(const Sprite &sprite) m_ui->labelObjectPreview->setPixmap(std::move(pixmap)); } -void RoomPropertiesDialog::objectNameChanged(const Object &object) +void RoomPropertiesDialog::objectNameChanged(const Object &object, const QString &oldName) { - if (!m_selectedObject) - return; + if (m_selectedObject && &object == m_selectedObject) + m_ui->lineEditObject->setText(object.name); - if (&object != m_selectedObject) - return; + for (auto &obj : m_objects) + { + if (obj.objectName != oldName) + continue; - m_ui->lineEditObject->setText(object.name); + obj.objectName = object.name; + } } void RoomPropertiesDialog::objectAboutToBeRemoved(const Object &object) { - if (!m_selectedObject) - return; + if (m_selectedObject && &object == m_selectedObject) + { + m_selectedObject = nullptr; + m_ui->lineEditObject->clear(); + m_ui->labelObjectPreview->setPixmap({}); + m_ui->roomEditWidget->setSelectedObject(nullptr); + } - if (&object != m_selectedObject) - return; - - m_selectedObject = nullptr; - m_ui->lineEditObject->clear(); - m_ui->labelObjectPreview->setPixmap({}); - m_ui->roomEditWidget->setSelectedObject(nullptr); + for (auto iter = std::begin(m_objects); iter != std::end(m_objects);) + if (iter->objectName == object.name) + iter = m_objects.erase(iter); + else + iter++; } void RoomPropertiesDialog::objectSpriteNameChanged(const Object &object) diff --git a/src/editor/dialogs/roompropertiesdialog.h b/src/editor/dialogs/roompropertiesdialog.h index ddb02b1..1d551e8 100644 --- a/src/editor/dialogs/roompropertiesdialog.h +++ b/src/editor/dialogs/roompropertiesdialog.h @@ -41,7 +41,7 @@ private slots: void roomNameChanged(const Room &room); void spritePixmapsChanged(const Sprite &sprite); - void objectNameChanged(const Object &object); + void objectNameChanged(const Object &object, const QString &oldName); void objectAboutToBeRemoved(const Object &object); void objectSpriteNameChanged(const Object &object); diff --git a/src/editor/icons/event-collision.png b/src/editor/icons/event-collision.png new file mode 100644 index 0000000000000000000000000000000000000000..c8a0c483f9603345bc7362192d5138daf29f9795 GIT binary patch literal 8081 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 literal 0 HcmV?d00001 diff --git a/src/editor/models/objecteventsmodel.cpp b/src/editor/models/objecteventsmodel.cpp index 3e26084..afc1acf 100644 --- a/src/editor/models/objecteventsmodel.cpp +++ b/src/editor/models/objecteventsmodel.cpp @@ -5,56 +5,78 @@ #include "futurecpp.h" -ObjectEventsModel::ObjectEventsModel(Object::events_container_t &events, QObject *parent) : +ObjectEventsModel::ObjectEventsModel(Object::events_container_t &events, Object::collision_events_container_t &collisionEvents, + QObject *parent) : QAbstractListModel{parent}, - m_events{events} + m_events{events}, + m_collisionEvents{collisionEvents} { } int ObjectEventsModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) - return m_events.size(); + return m_events.size() + m_collisionEvents.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()) + if (index.row() < 0) { qWarning() << "row out of bounds" << index.row(); return {}; } - - const auto &pair = *std::next(std::cbegin(m_events), index.row()); - - switch (role) + else if (std::size_t(index.row()) < m_events.size()) { - case Qt::DisplayRole: - case Qt::EditRole: - switch (pair.first) + const auto &pair = *std::next(std::cbegin(m_events), index.row()); + + switch (role) { - 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::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 {}; + } } - case Qt::DecorationRole: - switch (pair.first) + } + else if (std::size_t(index.row()) < m_collisionEvents.size() + m_events.size()) + { + const auto &pair = *std::next(std::cbegin(m_collisionEvents), index.row() - m_events.size()); + + switch (role) { - 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 {}; + case Qt::DisplayRole: + case Qt::EditRole: + return pair.first; + case Qt::DecorationRole: + return QIcon{":/qtgameengine/icons/event-collision.png"}; } } + else + { + qWarning() << "row out of bounds" << index.row(); + return {}; + } return {}; } @@ -63,27 +85,62 @@ bool ObjectEventsModel::removeRows(int row, int count, const QModelIndex &parent { Q_UNUSED(parent) - if (row < 0 || std::size_t(row) >= m_events.size()) + if (row < 0 || std::size_t(row) >= m_events.size() + m_collisionEvents.size()) { qWarning() << "unexpected row" << row; return false; } - if (count < 0 || std::size_t(count) > m_events.size() - row) + if (count < 0 || std::size_t(count) > m_events.size() + m_collisionEvents.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); + + if (std::size_t(row) < m_events.size()) + { + const auto remaining = m_events.size() - row; + const auto begin = std::next(std::begin(m_events), row); + const auto end = std::next(begin, std::min(count, remaining)); + m_events.erase(begin, end); + + if (std::size_t(count) > remaining) + { + count -= remaining; + + const auto begin = std::begin(m_collisionEvents); + const auto end = std::next(begin, count); + m_collisionEvents.erase(begin, end); + } + } + else if (std::size_t(row) < m_events.size() + m_collisionEvents.size()) + { + qDebug() << row - m_events.size(); + qDebug() << count; + qDebug() << m_collisionEvents.size(); + + const auto begin = std::next(std::begin(m_collisionEvents), row - m_events.size()); + const auto end = std::next(begin, count); + m_collisionEvents.erase(begin, end); + } + endRemoveRows(); return true; } +bool ObjectEventsModel::addEvent(std::variant eventType) +{ + if (const auto &type = std::get_if(&eventType)) + return addEvent(*type); + if (const auto &object = std::get_if(&eventType)) + return addEvent(*object); + qCritical() << "not implemented"; + return false; +} + bool ObjectEventsModel::addEvent(Object::EventType eventType) { if (m_events.contains(eventType)) @@ -122,6 +179,72 @@ bool ObjectEventsModel::addEvent(Object::EventType eventType) return true; } +bool ObjectEventsModel::addEvent(const QString &object) +{ + if (m_collisionEvents.contains(object)) + { + qWarning() << object << "duplicate"; + return false; + } + + // temporary copy to find row before inserting, as its needed for beginInsertRows() + auto tempevents = m_collisionEvents; + + const auto &tempInsertResult = tempevents.insert(std::make_pair(object, ActionsContainer{})); + if (!tempInsertResult.second) + { + qWarning() << "temp inserting failed!"; + return false; + } + + const auto tempNewRow = std::distance(std::begin(tempevents), tempInsertResult.first); + + + beginInsertRows({}, tempNewRow + m_events.size(), tempNewRow + m_events.size()); + + const auto &insertResult = m_collisionEvents.insert(std::make_pair(object, ActionsContainer{})); + if (!insertResult.second) + { + qWarning() << "inserting failed!"; + return false; + } + + const auto newRow = std::distance(std::begin(m_collisionEvents), insertResult.first); + Q_ASSERT(tempNewRow == newRow); + + endInsertRows(); + + return true; +} + +bool ObjectEventsModel::changeEvent(std::variant eventType, std::variant newEventType) +{ + if (const auto &type = std::get_if(&eventType)) + { + if (const auto &newType = std::get_if(&newEventType)) + return changeEvent(*type, *newType); + if (const auto &newObject = std::get_if(&newEventType)) + { + //return changeEvent(*type, *newObject); + } + qCritical() << "not implemented"; + return false; + } + if (const auto &object = std::get_if(&eventType)) + { + if (const auto &newType = std::get_if(&newEventType)) + { + //return changeEvent(*object, *newType); + } + if (const auto &newObject = std::get_if(&newEventType)) + return changeEvent(*object, *newObject); + qCritical() << "not implemented"; + return false; + } + qCritical() << "not implemented"; + return false; +} + bool ObjectEventsModel::changeEvent(Object::EventType eventType, Object::EventType newEventType) { const auto iter = m_events.find(eventType); @@ -137,6 +260,12 @@ bool ObjectEventsModel::changeEvent(Object::EventType eventType, Object::EventTy return true; } + if (m_events.contains(newEventType)) + { + qWarning() << std::to_underlying(eventType) << "duplicate"; + return false; + } + auto container = std::move(iter->second); const auto oldRow = std::distance(std::begin(m_events), iter); @@ -175,6 +304,75 @@ bool ObjectEventsModel::changeEvent(Object::EventType eventType, Object::EventTy return true; } +bool ObjectEventsModel::changeEvent(const QString &object, const QString &newObject) +{ + const auto iter = m_collisionEvents.find(object); + if (iter == std::cend(m_collisionEvents)) + { + qWarning() << object << "not found"; + return false; + } + + if (object == newObject) + { + qWarning() << "same event again"; + return true; + } + + if (m_collisionEvents.contains(newObject)) + { + qWarning() << newObject << "duplicate"; + return false; + } + + auto container = std::move(iter->second); + + const auto oldRow = std::distance(std::begin(m_collisionEvents), iter); + + beginRemoveRows({}, oldRow + m_events.size(), oldRow + m_events.size()); + m_collisionEvents.erase(iter); + endRemoveRows(); + + // temporary copy to find row before inserting, as its needed for beginInsertRows() + auto tempevents = m_collisionEvents; + + const auto &tempInsertResult = tempevents.insert(std::make_pair(newObject, ActionsContainer{})); + if (!tempInsertResult.second) + { + qWarning() << "temp inserting failed!"; + return false; + } + + const auto tempNewRow = std::distance(std::begin(tempevents), tempInsertResult.first); + + + beginInsertRows({}, tempNewRow + m_events.size(), tempNewRow + m_events.size()); + + const auto &insertResult = m_collisionEvents.insert(std::make_pair(newObject, std::move(container))); + if (!insertResult.second) + { + qWarning() << "inserting failed!"; + return false; + } + + const auto newRow = std::distance(std::begin(m_collisionEvents), insertResult.first); + Q_ASSERT(tempNewRow == newRow); + + endInsertRows(); + + return true; +} + +bool ObjectEventsModel::removeEvent(std::variant eventType) +{ + if (const auto &type = std::get_if(&eventType)) + return removeEvent(*type); + if (const auto &object = std::get_if(&eventType)) + return removeEvent(*object); + qCritical() << "not implemented"; + return false; +} + bool ObjectEventsModel::removeEvent(Object::EventType eventType) { const auto iter = m_events.find(eventType); @@ -193,46 +391,79 @@ bool ObjectEventsModel::removeEvent(Object::EventType eventType) return true; } -std::pair *ObjectEventsModel::getEvent(const QModelIndex &index) +bool ObjectEventsModel::removeEvent(const QString &object) +{ + Q_UNUSED(object); + qCritical() << "not implemented"; + return false; +} + +auto ObjectEventsModel::getEvent(const QModelIndex &index) -> get_event_result_t { if (!index.isValid()) { qWarning() << "unexpected invalid index"; - return nullptr; + return std::nullopt; } return getEvent(index.row()); } -const std::pair *ObjectEventsModel::getEvent(const QModelIndex &index) const +auto ObjectEventsModel::getEvent(const QModelIndex &index) const -> const_get_event_result_t { if (!index.isValid()) { qWarning() << "unexpected invalid index"; - return nullptr; + return std::nullopt; } return getEvent(index.row()); } -std::pair *ObjectEventsModel::getEvent(int row) +auto ObjectEventsModel::getEvent(int row) -> get_event_result_t { - if (row < 0 || std::size_t(row) >= m_events.size()) + if (row < 0) { qWarning() << "unexpected row" << row; - return nullptr; + return std::nullopt; } - - return &*std::next(std::begin(m_events), row); -} - -const std::pair *ObjectEventsModel::getEvent(int row) const -{ - if (row < 0 || std::size_t(row) >= m_events.size()) + else if (std::size_t(row) < m_events.size()) + { + auto iter = std::next(std::begin(m_events), row); + return std::make_pair(iter->first, std::ref(iter->second)); + } + else if (std::size_t(row) < m_events.size() + m_collisionEvents.size()) + { + auto iter = std::next(std::begin(m_collisionEvents), row - m_events.size()); + return std::make_pair(iter->first, std::ref(iter->second)); + } + else { qWarning() << "unexpected row" << row; - return nullptr; + return std::nullopt; + } +} + +auto ObjectEventsModel::getEvent(int row) const -> const_get_event_result_t +{ + if (row < 0) + { + qWarning() << "unexpected row" << row; + return std::nullopt; + } + else if (std::size_t(row) >= m_events.size()) + { + auto iter = std::next(std::cbegin(m_events), row); + return std::make_pair(iter->first, std::cref(iter->second)); + } + else if (std::size_t(row) >= m_events.size() + m_collisionEvents.size()) + { + auto iter = std::next(std::cbegin(m_collisionEvents), row - m_events.size()); + return std::make_pair(iter->first, std::cref(iter->second)); + } + else + { + qWarning() << "unexpected row" << row; + return std::nullopt; } - - return &*std::next(std::cbegin(m_events), row); } diff --git a/src/editor/models/objecteventsmodel.h b/src/editor/models/objecteventsmodel.h index 0e542a9..5dba891 100644 --- a/src/editor/models/objecteventsmodel.h +++ b/src/editor/models/objecteventsmodel.h @@ -11,21 +11,32 @@ class ObjectEventsModel : public QAbstractListModel Q_OBJECT public: - explicit ObjectEventsModel(Object::events_container_t &events, QObject *parent = nullptr); + explicit ObjectEventsModel(Object::events_container_t &events, Object::collision_events_container_t &collisionEvents, + 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(std::variant eventType); bool addEvent(Object::EventType eventType); + bool addEvent(const QString &object); + bool changeEvent(std::variant eventType, std::variant newEventType); bool changeEvent(Object::EventType eventType, Object::EventType newEventType); + bool changeEvent(const QString &object, const QString &newObject); + bool removeEvent(std::variant eventType); bool removeEvent(Object::EventType eventType); + bool removeEvent(const QString &object); - std::pair *getEvent(const QModelIndex &index); - const std::pair *getEvent(const QModelIndex &index) const; - std::pair *getEvent(int row); - const std::pair *getEvent(int row) const; + using get_event_result_t = std::optional, std::reference_wrapper>>; + using const_get_event_result_t = std::optional, std::reference_wrapper>>; + + get_event_result_t getEvent(const QModelIndex &index); + const_get_event_result_t getEvent(const QModelIndex &index) const; + get_event_result_t getEvent(int row); + const_get_event_result_t getEvent(int row) const; private: Object::events_container_t &m_events; + Object::collision_events_container_t &m_collisionEvents; }; diff --git a/src/editor/models/projecttreemodel.cpp b/src/editor/models/projecttreemodel.cpp index d4cf477..839ad9e 100644 --- a/src/editor/models/projecttreemodel.cpp +++ b/src/editor/models/projecttreemodel.cpp @@ -959,6 +959,16 @@ template<> void ProjectTreeModel::onBeforeRemove(const Sprite &sprite) } } +template<> void ProjectTreeModel::onBeforeRemove(const Object &object) +{ + for (auto &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 void ProjectTreeModel::onBeforeRename(const T &entry, const QString &newName) { Q_UNUSED(entry) @@ -973,22 +983,38 @@ template void ProjectTreeModel::onAfterRename(const T &entry, const template<> void ProjectTreeModel::onAfterRename(const Sprite &sprite, const QString &oldName) { - for (auto iter = std::begin(m_project->objects); iter != std::end(m_project->objects); iter++) + for (auto &object : m_project->objects) { - if (iter->spriteName != oldName) + if (object.spriteName != oldName) continue; - auto oldSpriteName = std::move(iter->spriteName); + auto oldSpriteName = std::move(object.spriteName); - iter->spriteName = sprite.name; + object.spriteName = sprite.name; // const auto index = this->index(std::distance(std::begin(m_project->objects), iter), 0, rootFor()); // emit dataChanged(index, index, {Qt::DecorationRole}); - emit objectSpriteNameChanged(*iter, std::move(oldSpriteName)); + emit objectSpriteNameChanged(object, std::move(oldSpriteName)); } } +template<> void ProjectTreeModel::onAfterRename(const Object &object, const QString &oldName) +{ + for (auto &room : m_project->rooms) + { + for (auto &obj : room.objects) + { + if (obj.objectName != oldName) + continue; + + obj.objectName = object.name; + } + } + + // TODO object collision events +} + template QString ProjectTreeModel::getFreeNameFor(const std::list &container) { diff --git a/src/editor/resources_editor.qrc b/src/editor/resources_editor.qrc index 8a7be93..c2f0725 100644 --- a/src/editor/resources_editor.qrc +++ b/src/editor/resources_editor.qrc @@ -76,6 +76,7 @@ icons/info.png icons/merge.png icons/sort.png + icons/event-collision.png icons/event-create.png icons/event-destroy.png icons/event-step.png diff --git a/src/projectcontainer.cpp b/src/projectcontainer.cpp index d2f14a5..56346d2 100644 --- a/src/projectcontainer.cpp +++ b/src/projectcontainer.cpp @@ -306,7 +306,8 @@ QDataStream &operator<<(QDataStream &ds, const Object &object) << object.solid << object.depth << object.persistent - << object.events; + << object.events + << object.collisionEvents; return ds; } @@ -318,7 +319,8 @@ QDataStream &operator>>(QDataStream &ds, Object &object) >> object.solid >> object.depth >> object.persistent - >> object.events; + >> object.events + >> object.collisionEvents; return ds; } diff --git a/src/projectcontainer.h b/src/projectcontainer.h index 15052c7..8d16478 100644 --- a/src/projectcontainer.h +++ b/src/projectcontainer.h @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -117,6 +118,7 @@ struct Object }; using events_container_t = std::map; + using collision_events_container_t = std::map; QString name; QString spriteName; @@ -125,6 +127,7 @@ struct Object int depth{}; bool persistent{}; events_container_t events; + collision_events_container_t collisionEvents; }; struct Room