Skip to content

Commit

Permalink
Perform changes to the world through the undo commands
Browse files Browse the repository at this point in the history
This also makes sure the UI adjusts immediately to the changes, since
the WorldManager::worldsChanged signal is emitted.

Also added some checks on the function parameters.
  • Loading branch information
bjorn committed Jan 16, 2024
1 parent 5edc0e0 commit 2292096
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 147 deletions.
68 changes: 1 addition & 67 deletions src/tiled/abstractworldtool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include "abstractworldtool.h"
#include "actionmanager.h"
#include "changeworld.h"
#include "documentmanager.h"
#include "mainwindow.h"
#include "map.h"
Expand All @@ -43,73 +44,6 @@

namespace Tiled {

class AddMapCommand : public QUndoCommand
{
public:
AddMapCommand(const QString &worldName, const QString &mapName, const QRect &rect)
: QUndoCommand(QCoreApplication::translate("Undo Commands", "Add Map to World"))
, mWorldName(worldName)
, mMapName(mapName)
, mRect(rect)
{
}

void undo() override
{
WorldManager::instance().removeMap(mMapName);
}

void redo() override
{
WorldManager::instance().addMap(mWorldName, mMapName, mRect);
}

private:
QString mWorldName;
QString mMapName;
QRect mRect;
};

class RemoveMapCommand : public QUndoCommand
{
public:
RemoveMapCommand(const QString &mapName)
: QUndoCommand(QCoreApplication::translate("Undo Commands", "Remove Map from World"))
, mMapName(mapName)
{
const WorldManager &manager = WorldManager::instance();
const World *world = manager.worldForMap(mMapName);
mPreviousRect = world->mapRect(mMapName);
mWorldName = world->fileName;
}

void undo() override
{
WorldManager::instance().addMap(mWorldName, mMapName, mPreviousRect);
}

void redo() override
{
// ensure we're switching to a different map in case the current map is removed
DocumentManager *manager = DocumentManager::instance();
if (manager->currentDocument() && manager->currentDocument()->fileName() == mMapName) {
const World *world = WorldManager::instance().worldForMap(mMapName);
for (World::MapEntry &entry : world->allMaps())
if (entry.fileName != mMapName) {
manager->switchToDocument(entry.fileName);
break;
}
}
WorldManager::instance().removeMap(mMapName);
}

private:
QString mWorldName;
QString mMapName;
QRect mPreviousRect;
};


AbstractWorldTool::AbstractWorldTool(Id id,
const QString &name,
const QIcon &icon,
Expand Down
102 changes: 102 additions & 0 deletions src/tiled/changeworld.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* changeworld.cpp
* Copyright 2019, Nils Kuebler <[email protected]>
* Copyright 2024, Thorbjørn Lindeijer <[email protected]>
*
* This file is part of Tiled.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "changeworld.h"

#include "documentmanager.h"
#include "worldmanager.h"

#include <QCoreApplication>

namespace Tiled {

AddMapCommand::AddMapCommand(const QString &worldName, const QString &mapName, const QRect &rect)
: QUndoCommand(QCoreApplication::translate("Undo Commands", "Add Map to World"))
, mWorldName(worldName)
, mMapName(mapName)
, mRect(rect)
{
}

void AddMapCommand::undo()
{
WorldManager::instance().removeMap(mMapName);
}

void AddMapCommand::redo()
{
qDebug() << Q_FUNC_INFO;
WorldManager::instance().addMap(mWorldName, mMapName, mRect);
}


RemoveMapCommand::RemoveMapCommand(const QString &mapName)
: QUndoCommand(QCoreApplication::translate("Undo Commands", "Remove Map from World"))
, mMapName(mapName)
{
const WorldManager &manager = WorldManager::instance();
const World *world = manager.worldForMap(mMapName);
mPreviousRect = world->mapRect(mMapName);
mWorldName = world->fileName;
}

void RemoveMapCommand::undo()
{
WorldManager::instance().addMap(mWorldName, mMapName, mPreviousRect);
}

void RemoveMapCommand::redo()
{
// ensure we're switching to a different map in case the current map is removed
DocumentManager *manager = DocumentManager::instance();
if (manager->currentDocument() && manager->currentDocument()->fileName() == mMapName) {
const World *world = WorldManager::instance().worldForMap(mMapName);
for (const World::MapEntry &entry : world->allMaps()) {
if (entry.fileName != mMapName) {
manager->switchToDocument(entry.fileName);
break;
}
}
}
WorldManager::instance().removeMap(mMapName);
}


SetMapRectCommand::SetMapRectCommand(const QString &mapName, QRect rect)
: QUndoCommand(QCoreApplication::translate("Undo Commands", "Move Map"))
, mMapName(mapName)
, mRect(rect)
{
const WorldManager &manager = WorldManager::instance();
mPreviousRect = manager.worldForMap(mMapName)->mapRect(mMapName);
}

void SetMapRectCommand::undo()
{
WorldManager::instance().setMapRect(mMapName, mPreviousRect);
}

void SetMapRectCommand::redo()
{
WorldManager::instance().setMapRect(mMapName, mRect);
}

} // namespace Tiled
71 changes: 71 additions & 0 deletions src/tiled/changeworld.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* changeworld.h
* Copyright 2019, Nils Kuebler <[email protected]>
* Copyright 2024, Thorbjørn Lindeijer <[email protected]>
*
* This file is part of Tiled.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include <QRect>
#include <QUndoCommand>

namespace Tiled {

class AddMapCommand : public QUndoCommand
{
public:
AddMapCommand(const QString &worldName, const QString &mapName, const QRect &rect);

void undo() override;
void redo() override;

private:
QString mWorldName;
QString mMapName;
QRect mRect;
};

class RemoveMapCommand : public QUndoCommand
{
public:
RemoveMapCommand(const QString &mapName);

void undo() override;
void redo() override;

private:
QString mWorldName;
QString mMapName;
QRect mPreviousRect;
};

class SetMapRectCommand : public QUndoCommand
{
public:
SetMapRectCommand(const QString &mapName, QRect rect);

void undo() override;
void redo() override;

private:
QString mMapName;
QRect mRect;
QRect mPreviousRect;
};

} // namespace Tiled
75 changes: 41 additions & 34 deletions src/tiled/editableworld.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,13 @@
*/

#include "editableworld.h"
#include "worlddocument.h"
#include "worldmanager.h"

namespace Tiled {
#include "changeworld.h"
#include "scriptmanager.h"

ScriptWorld::ScriptWorld(World *world)
: Object(WorldType)
, world(world)
{
}
#include <QUndoStack>

namespace Tiled {

EditableWorld::EditableWorld(WorldDocument *worldDocument, QObject *parent)
: EditableAsset(worldDocument, nullptr, parent)
Expand All @@ -38,22 +35,6 @@ EditableWorld::EditableWorld(WorldDocument *worldDocument, QObject *parent)
setObject(&mWorldObject);
}


ScriptWorldMapEntry::ScriptWorldMapEntry(World::MapEntry mapEntry)
{
mMapEntry = mapEntry;
}

QString ScriptWorldMapEntry::fileName() const
{
return mMapEntry.fileName;
}

QRect ScriptWorldMapEntry::rect() const
{
return mMapEntry.rect;
}

QString EditableWorld::displayName() const
{
return world()->displayName();
Expand All @@ -64,18 +45,18 @@ bool EditableWorld::containsMap(const QString &fileName)
return world()->containsMap(fileName);
}

QVector<ScriptWorldMapEntry*> EditableWorld::allMaps() const
QList<ScriptWorldMapEntry*> EditableWorld::maps() const
{
QVector<ScriptWorldMapEntry*> maps;
for (auto &entry : world()->allMaps())
QList<ScriptWorldMapEntry*> maps;
for (const auto &entry : std::as_const(world()->maps))
maps.append(new ScriptWorldMapEntry(entry));
return maps;
}

QVector<ScriptWorldMapEntry*> EditableWorld::mapsInRect(const QRect &rect)
QList<ScriptWorldMapEntry*> EditableWorld::mapsInRect(const QRect &rect)
{
QVector<ScriptWorldMapEntry*> maps;
for (auto &entry : world()->mapsInRect(rect))
QList<ScriptWorldMapEntry*> maps;
for (const auto &entry : world()->mapsInRect(rect))
maps.append(new ScriptWorldMapEntry(entry));
return maps;
}
Expand All @@ -89,23 +70,49 @@ int EditableWorld::mapIndex(const QString &fileName) const
{
return mWorldObject.world->mapIndex(fileName);
}

void EditableWorld::setMapRect(int mapIndex, const QRect &rect)
{
mWorldObject.world->setMapRect(mapIndex, rect);
if (mapIndex < 0 || mapIndex >= mWorldObject.world->maps.size()) {
ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Index out of range"));
return;
}

const QString &fileName = mWorldObject.world->maps.at(mapIndex).fileName;
document()->undoStack()->push(new SetMapRectCommand(fileName, rect));
}
void EditableWorld::addMap(const QString &fileName, const QRect &rect)

void EditableWorld::addMap(const QString &mapFileName, const QRect &rect)
{
mWorldObject.world->addMap(fileName, rect);
if (mapFileName.isEmpty()) {
ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Invalid argument"));
return;
}

if (WorldManager::instance().worldForMap(mapFileName)) {
ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Map is already part of a loaded world"));
return;
}

document()->undoStack()->push(new AddMapCommand(fileName(), mapFileName, rect));
}

void EditableWorld::removeMap(int mapIndex)
{
mWorldObject.world->removeMap(mapIndex);
if (mapIndex < 0 || mapIndex >= mWorldObject.world->maps.size()) {
ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Index out of range"));
return;
}

const QString &fileName = mWorldObject.world->maps.at(mapIndex).fileName;
document()->undoStack()->push(new RemoveMapCommand(fileName));
}

bool EditableWorld::save()
{
return WorldManager::instance().saveWorld(mWorldObject.world->fileName);
}

QSharedPointer<Document> EditableWorld::createDocument()
{
// We don't currently support opening a world in its own tab, which this
Expand Down
Loading

0 comments on commit 2292096

Please sign in to comment.